Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1020)

Side by Side Diff: Source/wtf/PartitionAlloc.cpp

Issue 1197753003: PartitionAlloc: implement discarding for partitionPurgeMemoryGeneric(). (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@rawsize
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/wtf/PartitionAlloc.h ('k') | Source/wtf/PartitionAllocTest.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 PartitionPage* secondaryPage = reinterpret_cast<PartitionPage*>(pageChar Ptr); 478 PartitionPage* secondaryPage = reinterpret_cast<PartitionPage*>(pageChar Ptr);
479 secondaryPage->pageOffset = i; 479 secondaryPage->pageOffset = i;
480 } 480 }
481 } 481 }
482 482
483 static ALWAYS_INLINE size_t partitionRoundUpToSystemPage(size_t size) 483 static ALWAYS_INLINE size_t partitionRoundUpToSystemPage(size_t size)
484 { 484 {
485 return (size + kSystemPageOffsetMask) & kSystemPageBaseMask; 485 return (size + kSystemPageOffsetMask) & kSystemPageBaseMask;
486 } 486 }
487 487
488 static ALWAYS_INLINE size_t partitionRoundDownToSystemPage(size_t size)
489 {
490 return size & kSystemPageBaseMask;
491 }
492
488 static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist(PartitionPage* page ) 493 static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist(PartitionPage* page )
489 { 494 {
490 ASSERT(page != &PartitionRootGeneric::gSeedPage); 495 ASSERT(page != &PartitionRootGeneric::gSeedPage);
491 uint16_t numSlots = page->numUnprovisionedSlots; 496 uint16_t numSlots = page->numUnprovisionedSlots;
492 ASSERT(numSlots); 497 ASSERT(numSlots);
493 PartitionBucket* bucket = page->bucket; 498 PartitionBucket* bucket = page->bucket;
494 // We should only get here when _every_ slot is either used or unprovisioned . 499 // We should only get here when _every_ slot is either used or unprovisioned .
495 // (The third state is "on the freelist". If we have a non-empty freelist, w e should not get here.) 500 // (The third state is "on the freelist". If we have a non-empty freelist, w e should not get here.)
496 ASSERT(numSlots + page->numAllocatedSlots == partitionBucketSlots(bucket)); 501 ASSERT(numSlots + page->numAllocatedSlots == partitionBucketSlots(bucket));
497 // Similarly, make explicitly sure that the freelist is empty. 502 // Similarly, make explicitly sure that the freelist is empty.
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after
870 static void partitionDecommitEmptyPages(PartitionRootBase* root) 875 static void partitionDecommitEmptyPages(PartitionRootBase* root)
871 { 876 {
872 for (size_t i = 0; i < kMaxFreeableSpans; ++i) { 877 for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
873 PartitionPage* page = root->globalEmptyPageRing[i]; 878 PartitionPage* page = root->globalEmptyPageRing[i];
874 if (page) 879 if (page)
875 partitionDecommitPageIfPossible(root, page); 880 partitionDecommitPageIfPossible(root, page);
876 root->globalEmptyPageRing[i] = nullptr; 881 root->globalEmptyPageRing[i] = nullptr;
877 } 882 }
878 } 883 }
879 884
880 void partitionPurgeMemory(PartitionRoot* root, int flags)
881 {
882 if (flags & PartitionPurgeDecommitEmptyPages)
883 partitionDecommitEmptyPages(root);
884 }
885
886 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags)
887 {
888 spinLockLock(&root->lock);
889 if (flags & PartitionPurgeDecommitEmptyPages)
890 partitionDecommitEmptyPages(root);
891 spinLockUnlock(&root->lock);
892 }
893
894 void partitionFreeSlowPath(PartitionPage* page) 885 void partitionFreeSlowPath(PartitionPage* page)
895 { 886 {
896 PartitionBucket* bucket = page->bucket; 887 PartitionBucket* bucket = page->bucket;
897 ASSERT(page != &PartitionRootGeneric::gSeedPage); 888 ASSERT(page != &PartitionRootGeneric::gSeedPage);
898 if (LIKELY(page->numAllocatedSlots == 0)) { 889 if (LIKELY(page->numAllocatedSlots == 0)) {
899 // Page became fully unused. 890 // Page became fully unused.
900 if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) { 891 if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) {
901 partitionDirectUnmap(page); 892 partitionDirectUnmap(page);
902 return; 893 return;
903 } 894 }
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
1064 size_t copySize = actualOldSize; 1055 size_t copySize = actualOldSize;
1065 if (newSize < copySize) 1056 if (newSize < copySize)
1066 copySize = newSize; 1057 copySize = newSize;
1067 1058
1068 memcpy(ret, ptr, copySize); 1059 memcpy(ret, ptr, copySize);
1069 partitionFreeGeneric(root, ptr); 1060 partitionFreeGeneric(root, ptr);
1070 return ret; 1061 return ret;
1071 #endif 1062 #endif
1072 } 1063 }
1073 1064
1074 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page) 1065 static void partitionPageWalk(PartitionBucketMemoryStats* statsOut, const Partit ionPage* page, bool discardMemory)
haraken 2015/06/22 03:36:58 Maybe you can return the discardableBytes from thi
1066 {
1067 const PartitionBucket* bucket = page->bucket;
1068 if (bucket->slotSize < kSystemPageSize)
1069 return;
1070
1071 size_t bucketNumSlots = partitionBucketSlots(bucket);
1072 char slotUsage[(kPartitionPageSize * kMaxPartitionPagesPerSlotSpan) / kSyste mPageSize];
1073 size_t lastSlot = -1;
1074 memset(slotUsage, 1, sizeof(slotUsage));
1075 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page));
1076 PartitionFreelistEntry* fl = page->freelistHead;
1077 // First, walk the freelist for this page and make a bitmap of which slots
1078 // are not in use.
1079 while (fl) {
1080 size_t slotIndex = (reinterpret_cast<char*>(fl) - ptr) / bucket->slotSiz e;
1081 ASSERT(slotIndex < bucketNumSlots);
1082 slotUsage[slotIndex] = 0;
1083 fl = partitionFreelistMask(fl->next);
1084 if (!fl && !partitionFreelistMask(fl))
1085 lastSlot = slotIndex;
1086 }
1087 // Next, walk the slots and for any not in use, consider where the system
1088 // page boundaries occur. We can release any system pages back to the
1089 // system as long as we don't interfere with a freelist pointer or an
1090 // adjacent slot.
1091 // TODO(cevans): I think we can "truncate" the page, i.e. increase the
1092 // value of page->numUnprovisionedSlots and rewrite(!) the freelist, if
1093 // we find that to be a win too.
1094 for (size_t i = 0; i < bucketNumSlots; ++i) {
1095 if (slotUsage[i])
1096 continue;
1097 // The first address we can safely discard is just after the freelist
1098 // pointer. There's one quirk: if the freelist pointer is actually a
1099 // null, we can discard that pointer value too.
1100 char* beginPtr = ptr + (i * bucket->slotSize);
1101 char* endPtr = beginPtr + bucket->slotSize;
1102 if (i != lastSlot)
1103 beginPtr += sizeof(PartitionFreelistEntry);
1104 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr)));
1105 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp ret_cast<size_t>(endPtr)));
1106 if (beginPtr < endPtr) {
1107 size_t discardable = endPtr - beginPtr;
1108 statsOut->discardableBytes += discardable;
1109 if (discardMemory)
1110 discardSystemPages(beginPtr, discardable);
1111 }
1112 }
1113 }
1114
1115 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page, bool discardMemory)
1075 { 1116 {
1076 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); 1117 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket);
1077 1118
1078 if (!page->freelistHead && page->numAllocatedSlots == 0) { 1119 if (!page->freelistHead && page->numAllocatedSlots == 0) {
1079 ASSERT(!page->numUnprovisionedSlots); 1120 ASSERT(!page->numUnprovisionedSlots);
1080 ++statsOut->numDecommittedPages; 1121 ++statsOut->numDecommittedPages;
1122 return;
1123 }
1124
1125 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa ge->numUnprovisionedSlots) * statsOut->bucketSlotSize);
1126
1127 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page));
1128 if (rawSize) {
1129 uint32_t activeBytes = static_cast<uint32_t>(partitionRoundUpToSystemPag e(rawSize));
1130 statsOut->activeBytes += activeBytes;
1131 if (activeBytes < pageBytesResident) {
1132 ASSERT(((pageBytesResident - activeBytes) % kSystemPageSize) == 0);
haraken 2015/06/22 03:36:58 Just help me understand: How is it guaranteed that
1133 size_t discardable = pageBytesResident - activeBytes;
1134 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page));
1135 ptr += activeBytes;
1136 statsOut->discardableBytes += discardable;
1137 if (discardMemory)
1138 discardSystemPages(ptr, discardable);
haraken 2015/06/22 03:36:58 I'm a bit afraid that if we have a bug in the disc
1139 }
1081 } else { 1140 } else {
1082 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page )); 1141 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot Size);
1083 if (rawSize) 1142 }
1084 statsOut->activeBytes += static_cast<uint32_t>(partitionRoundUpToSys temPage(rawSize)); 1143 statsOut->residentBytes += pageBytesResident;
1085 else 1144 if (!page->numAllocatedSlots) {
1086 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucket SlotSize); 1145 statsOut->decommittableBytes += pageBytesResident;
1087 size_t pageBytesResident = (bucketNumSlots - page->numUnprovisionedSlots ) * statsOut->bucketSlotSize; 1146 ++statsOut->numEmptyPages;
haraken 2015/06/22 03:36:58 Shall we call discardSystemPages here? Given that
1088 // Round up to system page size. 1147 } else if (page->numAllocatedSlots == bucketNumSlots) {
1089 size_t pageBytesResidentRounded = partitionRoundUpToSystemPage(pageBytes Resident); 1148 ++statsOut->numFullPages;
1090 statsOut->residentBytes += pageBytesResidentRounded; 1149 } else {
1091 if (!page->numAllocatedSlots) { 1150 ++statsOut->numActivePages;
1092 statsOut->decommittableBytes += pageBytesResidentRounded; 1151 partitionPageWalk(statsOut, page, discardMemory);
1093 ++statsOut->numEmptyPages;
1094 } else if (page->numAllocatedSlots == bucketNumSlots) {
1095 ++statsOut->numFullPages;
1096 } else {
1097 ++statsOut->numActivePages;
1098 }
1099 } 1152 }
1100 } 1153 }
1101 1154
1102 static void partitionDumpBucketStats(PartitionBucketMemoryStats* statsOut, const PartitionBucket* bucket) 1155 static void partitionDumpBucketStats(PartitionBucketMemoryStats* statsOut, const PartitionBucket* bucket, bool discardMemory)
1103 { 1156 {
1104 ASSERT(!partitionBucketIsDirectMapped(bucket)); 1157 ASSERT(!partitionBucketIsDirectMapped(bucket));
1105 statsOut->isValid = false; 1158 statsOut->isValid = false;
1106 // If the active page list is empty (== &PartitionRootGeneric::gSeedPage), 1159 // If the active page list is empty (== &PartitionRootGeneric::gSeedPage),
1107 // the bucket might still need to be reported if it has an empty page list, 1160 // the bucket might still need to be reported if it has an empty page list,
1108 // or full pages. 1161 // or full pages.
1109 if (bucket->activePagesHead == &PartitionRootGeneric::gSeedPage && !bucket-> emptyPagesHead && !bucket->numFullPages) 1162 if (bucket->activePagesHead == &PartitionRootGeneric::gSeedPage && !bucket-> emptyPagesHead && !bucket->numFullPages)
1110 return; 1163 return;
1111 1164
1112 memset(statsOut, '\0', sizeof(*statsOut)); 1165 memset(statsOut, '\0', sizeof(*statsOut));
1113 statsOut->isValid = true; 1166 statsOut->isValid = true;
1114 statsOut->isDirectMap = false; 1167 statsOut->isDirectMap = false;
1115 statsOut->numFullPages = static_cast<size_t>(bucket->numFullPages); 1168 statsOut->numFullPages = static_cast<size_t>(bucket->numFullPages);
1116 statsOut->bucketSlotSize = bucket->slotSize; 1169 statsOut->bucketSlotSize = bucket->slotSize;
1117 uint16_t bucketNumSlots = partitionBucketSlots(bucket); 1170 uint16_t bucketNumSlots = partitionBucketSlots(bucket);
1118 size_t bucketUsefulStorage = statsOut->bucketSlotSize * bucketNumSlots; 1171 size_t bucketUsefulStorage = statsOut->bucketSlotSize * bucketNumSlots;
1119 statsOut->allocatedPageSize = partitionBucketBytes(bucket); 1172 statsOut->allocatedPageSize = partitionBucketBytes(bucket);
1120 statsOut->activeBytes = bucket->numFullPages * bucketUsefulStorage; 1173 statsOut->activeBytes = bucket->numFullPages * bucketUsefulStorage;
1121 statsOut->residentBytes = bucket->numFullPages * statsOut->allocatedPageSize ; 1174 statsOut->residentBytes = bucket->numFullPages * statsOut->allocatedPageSize ;
1122 1175
1123 for (const PartitionPage* page = bucket->emptyPagesHead; page; page = page-> nextPage) { 1176 for (const PartitionPage* page = bucket->emptyPagesHead; page; page = page-> nextPage) {
1124 // Currently, only decommitted pages appear in the empty pages list. 1177 // Currently, only decommitted pages appear in the empty pages list.
1125 // This may change. 1178 // This may change.
1126 ASSERT(page != &PartitionRootGeneric::gSeedPage); 1179 ASSERT(page != &PartitionRootGeneric::gSeedPage);
1127 ASSERT(!page->freelistHead); 1180 ASSERT(!page->freelistHead);
1128 ASSERT(!page->numAllocatedSlots); 1181 ASSERT(!page->numAllocatedSlots);
1129 ASSERT(!page->numUnprovisionedSlots); 1182 ASSERT(!page->numUnprovisionedSlots);
1130 partitionDumpPageStats(statsOut, page); 1183 partitionDumpPageStats(statsOut, page, discardMemory);
1131 } 1184 }
1132 1185
1133 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { 1186 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) {
1134 for (const PartitionPage* page = bucket->activePagesHead; page; page = p age->nextPage) { 1187 for (const PartitionPage* page = bucket->activePagesHead; page; page = p age->nextPage) {
1135 ASSERT(page != &PartitionRootGeneric::gSeedPage); 1188 ASSERT(page != &PartitionRootGeneric::gSeedPage);
1136 partitionDumpPageStats(statsOut, page); 1189 partitionDumpPageStats(statsOut, page, discardMemory);
1137 } 1190 }
1138 } 1191 }
1139 } 1192 }
1140 1193
1141 void partitionDumpStatsGeneric(PartitionRootGeneric* partition, const char* part itionName, PartitionStatsDumper* partitionStatsDumper) 1194 static void partitionGenericBucketWalk(PartitionBucketMemoryStats* statsOut, Par titionRootGeneric* partition, bool discardMemory)
1142 { 1195 {
1143 PartitionBucketMemoryStats bucketStats[kGenericNumBuckets];
1144 spinLockLock(&partition->lock);
1145 for (size_t i = 0; i < kGenericNumBuckets; ++i) { 1196 for (size_t i = 0; i < kGenericNumBuckets; ++i) {
1146 const PartitionBucket* bucket = &partition->buckets[i]; 1197 const PartitionBucket* bucket = &partition->buckets[i];
1147 // Don't report the pseudo buckets that the generic allocator sets up in 1198 // Don't report the pseudo buckets that the generic allocator sets up in
1148 // order to preserve a fast size->bucket map (see 1199 // order to preserve a fast size->bucket map (see
1149 // partitionAllocGenericInit for details). 1200 // partitionAllocGenericInit for details).
1150 if (!bucket->activePagesHead) 1201 if (!bucket->activePagesHead)
1151 bucketStats[i].isValid = false; 1202 statsOut[i].isValid = false;
1152 else 1203 else
1153 partitionDumpBucketStats(&bucketStats[i], bucket); 1204 partitionDumpBucketStats(&statsOut[i], bucket, discardMemory);
1154 } 1205 }
1206 }
1155 1207
1208 void partitionPurgeMemory(PartitionRoot* root, int flags)
1209 {
1210 if (flags & PartitionPurgeDecommitEmptyPages)
1211 partitionDecommitEmptyPages(root);
haraken 2015/06/22 03:36:59 The reason you don't call partitionGenericBucketWa
1212 }
1213
1214 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags)
1215 {
1216 spinLockLock(&root->lock);
1217 if (flags & PartitionPurgeDecommitEmptyPages)
1218 partitionDecommitEmptyPages(root);
1219 if (flags & PartitionPurgeDiscardUnusedSystemPages) {
1220 PartitionBucketMemoryStats bucketStats[kGenericNumBuckets];
haraken 2015/06/22 03:36:58 It is redundant to prepare an array of dummy bucke
1221 partitionGenericBucketWalk(bucketStats, root, true);
haraken 2015/06/22 03:36:58 Or you can pass a nullptr to statsOut and remove t
1222 }
1223 spinLockUnlock(&root->lock);
1224 }
1225
1226 void partitionDumpStatsGeneric(PartitionRootGeneric* partition, const char* part itionName, PartitionStatsDumper* partitionStatsDumper)
1227 {
1228 PartitionBucketMemoryStats bucketStats[kGenericNumBuckets];
1156 static const size_t kMaxReportableDirectMaps = 4096; 1229 static const size_t kMaxReportableDirectMaps = 4096;
1157 uint32_t directMapLengths[kMaxReportableDirectMaps]; 1230 uint32_t directMapLengths[kMaxReportableDirectMaps];
1158 size_t numDirectMappedAllocations = 0; 1231 size_t numDirectMappedAllocations = 0;
1232
1233 spinLockLock(&partition->lock);
1234
1235 partitionGenericBucketWalk(bucketStats, partition, false);
1236
1159 for (PartitionDirectMapExtent* extent = partition->directMapList; extent; ex tent = extent->nextExtent) { 1237 for (PartitionDirectMapExtent* extent = partition->directMapList; extent; ex tent = extent->nextExtent) {
1160 ASSERT(!extent->nextExtent || extent->nextExtent->prevExtent == extent); 1238 ASSERT(!extent->nextExtent || extent->nextExtent->prevExtent == extent);
1161 directMapLengths[numDirectMappedAllocations] = extent->bucket->slotSize; 1239 directMapLengths[numDirectMappedAllocations] = extent->bucket->slotSize;
1162 ++numDirectMappedAllocations; 1240 ++numDirectMappedAllocations;
1163 if (numDirectMappedAllocations == kMaxReportableDirectMaps) 1241 if (numDirectMappedAllocations == kMaxReportableDirectMaps)
1164 break; 1242 break;
1165 } 1243 }
1166 1244
1167 spinLockUnlock(&partition->lock); 1245 spinLockUnlock(&partition->lock);
1168 1246
(...skipping 20 matching lines...) Expand all
1189 } 1267 }
1190 1268
1191 void partitionDumpStats(PartitionRoot* partition, const char* partitionName, Par titionStatsDumper* partitionStatsDumper) 1269 void partitionDumpStats(PartitionRoot* partition, const char* partitionName, Par titionStatsDumper* partitionStatsDumper)
1192 { 1270 {
1193 static const size_t kMaxReportableBuckets = 4096 / sizeof(void*); 1271 static const size_t kMaxReportableBuckets = 4096 / sizeof(void*);
1194 PartitionBucketMemoryStats memoryStats[kMaxReportableBuckets]; 1272 PartitionBucketMemoryStats memoryStats[kMaxReportableBuckets];
1195 const size_t partitionNumBuckets = partition->numBuckets; 1273 const size_t partitionNumBuckets = partition->numBuckets;
1196 ASSERT(partitionNumBuckets <= kMaxReportableBuckets); 1274 ASSERT(partitionNumBuckets <= kMaxReportableBuckets);
1197 1275
1198 for (size_t i = 0; i < partitionNumBuckets; ++i) 1276 for (size_t i = 0; i < partitionNumBuckets; ++i)
1199 partitionDumpBucketStats(&memoryStats[i], &partition->buckets()[i]); 1277 partitionDumpBucketStats(&memoryStats[i], &partition->buckets()[i], fals e);
1200 1278
1201 // partitionsDumpBucketStats is called after collecting stats because it 1279 // partitionsDumpBucketStats is called after collecting stats because it
1202 // can use PartitionAlloc to allocate and this can affect the statistics. 1280 // can use PartitionAlloc to allocate and this can affect the statistics.
1203 for (size_t i = 0; i < partitionNumBuckets; ++i) { 1281 for (size_t i = 0; i < partitionNumBuckets; ++i) {
1204 if (memoryStats[i].isValid) 1282 if (memoryStats[i].isValid)
1205 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]); 1283 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]);
1206 } 1284 }
1207 } 1285 }
1208 1286
1209 } // namespace WTF 1287 } // namespace WTF
1210 1288
OLDNEW
« no previous file with comments | « Source/wtf/PartitionAlloc.h ('k') | Source/wtf/PartitionAllocTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698