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

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: Fix Windows compile error. 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 473 matching lines...) Expand 10 before | Expand all | Expand 10 after
484 PartitionPage* secondaryPage = reinterpret_cast<PartitionPage*>(pageChar Ptr); 484 PartitionPage* secondaryPage = reinterpret_cast<PartitionPage*>(pageChar Ptr);
485 secondaryPage->pageOffset = i; 485 secondaryPage->pageOffset = i;
486 } 486 }
487 } 487 }
488 488
489 static ALWAYS_INLINE size_t partitionRoundUpToSystemPage(size_t size) 489 static ALWAYS_INLINE size_t partitionRoundUpToSystemPage(size_t size)
490 { 490 {
491 return (size + kSystemPageOffsetMask) & kSystemPageBaseMask; 491 return (size + kSystemPageOffsetMask) & kSystemPageBaseMask;
492 } 492 }
493 493
494 static ALWAYS_INLINE size_t partitionRoundDownToSystemPage(size_t size)
495 {
496 return size & kSystemPageBaseMask;
497 }
498
494 static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist(PartitionPage* page ) 499 static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist(PartitionPage* page )
495 { 500 {
496 ASSERT(page != &PartitionRootGeneric::gSeedPage); 501 ASSERT(page != &PartitionRootGeneric::gSeedPage);
497 uint16_t numSlots = page->numUnprovisionedSlots; 502 uint16_t numSlots = page->numUnprovisionedSlots;
498 ASSERT(numSlots); 503 ASSERT(numSlots);
499 PartitionBucket* bucket = page->bucket; 504 PartitionBucket* bucket = page->bucket;
500 // We should only get here when _every_ slot is either used or unprovisioned . 505 // We should only get here when _every_ slot is either used or unprovisioned .
501 // (The third state is "on the freelist". If we have a non-empty freelist, w e should not get here.) 506 // (The third state is "on the freelist". If we have a non-empty freelist, w e should not get here.)
502 ASSERT(numSlots + page->numAllocatedSlots == partitionBucketSlots(bucket)); 507 ASSERT(numSlots + page->numAllocatedSlots == partitionBucketSlots(bucket));
503 // Similarly, make explicitly sure that the freelist is empty. 508 // Similarly, make explicitly sure that the freelist is empty.
(...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after
891 static void partitionDecommitEmptyPages(PartitionRootBase* root) 896 static void partitionDecommitEmptyPages(PartitionRootBase* root)
892 { 897 {
893 for (size_t i = 0; i < kMaxFreeableSpans; ++i) { 898 for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
894 PartitionPage* page = root->globalEmptyPageRing[i]; 899 PartitionPage* page = root->globalEmptyPageRing[i];
895 if (page) 900 if (page)
896 partitionDecommitPageIfPossible(root, page); 901 partitionDecommitPageIfPossible(root, page);
897 root->globalEmptyPageRing[i] = nullptr; 902 root->globalEmptyPageRing[i] = nullptr;
898 } 903 }
899 } 904 }
900 905
901 void partitionPurgeMemory(PartitionRoot* root, int flags)
902 {
903 if (flags & PartitionPurgeDecommitEmptyPages)
904 partitionDecommitEmptyPages(root);
905 }
906
907 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags)
908 {
909 spinLockLock(&root->lock);
910 if (flags & PartitionPurgeDecommitEmptyPages)
911 partitionDecommitEmptyPages(root);
912 spinLockUnlock(&root->lock);
913 }
914
915 void partitionFreeSlowPath(PartitionPage* page) 906 void partitionFreeSlowPath(PartitionPage* page)
916 { 907 {
917 PartitionBucket* bucket = page->bucket; 908 PartitionBucket* bucket = page->bucket;
918 ASSERT(page != &PartitionRootGeneric::gSeedPage); 909 ASSERT(page != &PartitionRootGeneric::gSeedPage);
919 if (LIKELY(page->numAllocatedSlots == 0)) { 910 if (LIKELY(page->numAllocatedSlots == 0)) {
920 // Page became fully unused. 911 // Page became fully unused.
921 if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) { 912 if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) {
922 partitionDirectUnmap(page); 913 partitionDirectUnmap(page);
923 return; 914 return;
924 } 915 }
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
1063 size_t copySize = actualOldSize; 1054 size_t copySize = actualOldSize;
1064 if (newSize < copySize) 1055 if (newSize < copySize)
1065 copySize = newSize; 1056 copySize = newSize;
1066 1057
1067 memcpy(ret, ptr, copySize); 1058 memcpy(ret, ptr, copySize);
1068 partitionFreeGeneric(root, ptr); 1059 partitionFreeGeneric(root, ptr);
1069 return ret; 1060 return ret;
1070 #endif 1061 #endif
1071 } 1062 }
1072 1063
1064 static size_t partitionPurgePage(const PartitionPage* page, bool discard)
1065 {
1066 const PartitionBucket* bucket = page->bucket;
1067 if (bucket->slotSize < kSystemPageSize || !page->numAllocatedSlots)
1068 return 0;
1069
1070 size_t bucketNumSlots = partitionBucketSlots(bucket);
1071 size_t discardableBytes = 0;
1072
1073 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page));
1074 if (rawSize) {
1075 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage( rawSize));
1076 discardableBytes = bucket->slotSize - usedBytes;
1077 if (discardableBytes && discard) {
1078 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page));
1079 ptr += usedBytes;
1080 discardSystemPages(ptr, discardableBytes);
1081 }
1082 return discardableBytes;
1083 }
1084
1085 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS pan) / kSystemPageSize;
1086 ASSERT(bucketNumSlots <= maxSlotCount);
1087 char slotUsage[maxSlotCount];
1088 size_t lastSlot = static_cast<size_t>(-1);
1089 memset(slotUsage, 1, sizeof(slotUsage));
1090 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page));
1091 PartitionFreelistEntry* entry = page->freelistHead;
1092 // First, walk the freelist for this page and make a bitmap of which slots
1093 // are not in use.
1094 while (entry) {
1095 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / bucket->slot Size;
1096 ASSERT(slotIndex < bucketNumSlots);
1097 slotUsage[slotIndex] = 0;
1098 entry = partitionFreelistMask(entry->next);
1099 // If we have a slot where the masked freelist entry is 0, we can
1100 // actually discard that freelist entry because touching a discarded
1101 // page is guaranteed to return original content or 0.
1102 // (Note that this optimization won't fire on big endian machines
1103 // because the masking function is negation.)
1104 if (!partitionFreelistMask(entry))
1105 lastSlot = slotIndex;
1106 }
1107 // Next, walk the slots and for any not in use, consider where the system
1108 // page boundaries occur. We can release any system pages back to the
1109 // system as long as we don't interfere with a freelist pointer or an
1110 // adjacent slot.
1111 // TODO(cevans): I think we can "truncate" the page, i.e. increase the
1112 // value of page->numUnprovisionedSlots and rewrite(!) the freelist, if
1113 // we find that to be a win too.
1114 for (size_t i = 0; i < bucketNumSlots; ++i) {
1115 if (slotUsage[i])
1116 continue;
1117 // The first address we can safely discard is just after the freelist
1118 // pointer. There's one quirk: if the freelist pointer is actually a
1119 // null, we can discard that pointer value too.
1120 char* beginPtr = ptr + (i * bucket->slotSize);
1121 char* endPtr = beginPtr + bucket->slotSize;
1122 if (i != lastSlot)
1123 beginPtr += sizeof(PartitionFreelistEntry);
1124 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr)));
1125 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp ret_cast<size_t>(endPtr)));
1126 if (beginPtr < endPtr) {
1127 size_t partialSlotBytes = endPtr - beginPtr;
1128 discardableBytes += partialSlotBytes;
1129 if (discard)
1130 discardSystemPages(beginPtr, partialSlotBytes);
1131 }
1132 }
1133 return discardableBytes;
1134 }
1135
1136 static void partitionPurgeBucket(const PartitionBucket* bucket)
1137 {
1138 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) {
1139 for (const PartitionPage* page = bucket->activePagesHead; page; page = p age->nextPage) {
1140 ASSERT(page != &PartitionRootGeneric::gSeedPage);
1141 (void) partitionPurgePage(page, true);
1142 }
1143 }
1144 }
1145
1146 void partitionPurgeMemory(PartitionRoot* root, int flags)
1147 {
1148 if (flags & PartitionPurgeDecommitEmptyPages)
1149 partitionDecommitEmptyPages(root);
1150 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages
1151 // here because that flag is only useful for allocations >= system page
1152 // size. We only have allocations that large inside generic partitions
1153 // at the moment.
1154 }
1155
1156 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags)
1157 {
1158 spinLockLock(&root->lock);
1159 if (flags & PartitionPurgeDecommitEmptyPages)
1160 partitionDecommitEmptyPages(root);
1161 if (flags & PartitionPurgeDiscardUnusedSystemPages) {
1162 for (size_t i = 0; i < kGenericNumBuckets; ++i) {
1163 const PartitionBucket* bucket = &root->buckets[i];
1164 if (bucket->slotSize >= kSystemPageSize)
1165 partitionPurgeBucket(bucket);
1166 }
1167 }
1168 spinLockUnlock(&root->lock);
1169 }
1170
1073 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page) 1171 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page)
1074 { 1172 {
1075 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); 1173 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket);
1076 1174
1077 if (partitionPageStateIsDecommitted(page)) { 1175 if (partitionPageStateIsDecommitted(page)) {
1078 ++statsOut->numDecommittedPages; 1176 ++statsOut->numDecommittedPages;
1177 return;
1178 }
1179
1180 statsOut->discardableBytes += partitionPurgePage(page, false);
1181
1182 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page));
1183 if (rawSize)
1184 statsOut->activeBytes += static_cast<uint32_t>(rawSize);
1185 else
1186 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot Size);
1187
1188 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa ge->numUnprovisionedSlots) * statsOut->bucketSlotSize);
1189 statsOut->residentBytes += pageBytesResident;
1190 if (partitionPageStateIsEmpty(page)) {
1191 statsOut->decommittableBytes += pageBytesResident;
1192 ++statsOut->numEmptyPages;
1193 } else if (page->numAllocatedSlots == bucketNumSlots) {
1194 ++statsOut->numFullPages;
1079 } else { 1195 } else {
1080 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page )); 1196 ASSERT(page->numAllocatedSlots > 0);
1081 if (rawSize) 1197 ++statsOut->numActivePages;
1082 statsOut->activeBytes += static_cast<uint32_t>(partitionRoundUpToSys temPage(rawSize));
1083 else
1084 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucket SlotSize);
1085 size_t pageBytesResident = (bucketNumSlots - page->numUnprovisionedSlots ) * statsOut->bucketSlotSize;
1086 // Round up to system page size.
1087 size_t pageBytesResidentRounded = partitionRoundUpToSystemPage(pageBytes Resident);
1088 statsOut->residentBytes += pageBytesResidentRounded;
1089 if (partitionPageStateIsEmpty(page)) {
1090 statsOut->decommittableBytes += pageBytesResidentRounded;
1091 ++statsOut->numEmptyPages;
1092 } else if (page->numAllocatedSlots == bucketNumSlots) {
1093 ++statsOut->numFullPages;
1094 } else {
1095 ASSERT(page->numAllocatedSlots > 0);
1096 ++statsOut->numActivePages;
1097 }
1098 } 1198 }
1099 } 1199 }
1100 1200
1101 static void partitionDumpBucketStats(PartitionBucketMemoryStats* statsOut, const PartitionBucket* bucket) 1201 static void partitionDumpBucketStats(PartitionBucketMemoryStats* statsOut, const PartitionBucket* bucket)
1102 { 1202 {
1103 ASSERT(!partitionBucketIsDirectMapped(bucket)); 1203 ASSERT(!partitionBucketIsDirectMapped(bucket));
1104 statsOut->isValid = false; 1204 statsOut->isValid = false;
1105 // If the active page list is empty (== &PartitionRootGeneric::gSeedPage), 1205 // If the active page list is empty (== &PartitionRootGeneric::gSeedPage),
1106 // the bucket might still need to be reported if it has a list of empty, 1206 // the bucket might still need to be reported if it has a list of empty,
1107 // decommitted or full pages. 1207 // decommitted or full pages.
(...skipping 24 matching lines...) Expand all
1132 for (const PartitionPage* page = bucket->activePagesHead; page; page = p age->nextPage) { 1232 for (const PartitionPage* page = bucket->activePagesHead; page; page = p age->nextPage) {
1133 ASSERT(page != &PartitionRootGeneric::gSeedPage); 1233 ASSERT(page != &PartitionRootGeneric::gSeedPage);
1134 partitionDumpPageStats(statsOut, page); 1234 partitionDumpPageStats(statsOut, page);
1135 } 1235 }
1136 } 1236 }
1137 } 1237 }
1138 1238
1139 void partitionDumpStatsGeneric(PartitionRootGeneric* partition, const char* part itionName, PartitionStatsDumper* partitionStatsDumper) 1239 void partitionDumpStatsGeneric(PartitionRootGeneric* partition, const char* part itionName, PartitionStatsDumper* partitionStatsDumper)
1140 { 1240 {
1141 PartitionBucketMemoryStats bucketStats[kGenericNumBuckets]; 1241 PartitionBucketMemoryStats bucketStats[kGenericNumBuckets];
1242 static const size_t kMaxReportableDirectMaps = 4096;
1243 uint32_t directMapLengths[kMaxReportableDirectMaps];
1244 size_t numDirectMappedAllocations = 0;
1245
1142 spinLockLock(&partition->lock); 1246 spinLockLock(&partition->lock);
1247
1143 for (size_t i = 0; i < kGenericNumBuckets; ++i) { 1248 for (size_t i = 0; i < kGenericNumBuckets; ++i) {
1144 const PartitionBucket* bucket = &partition->buckets[i]; 1249 const PartitionBucket* bucket = &partition->buckets[i];
1145 // Don't report the pseudo buckets that the generic allocator sets up in 1250 // Don't report the pseudo buckets that the generic allocator sets up in
1146 // order to preserve a fast size->bucket map (see 1251 // order to preserve a fast size->bucket map (see
1147 // partitionAllocGenericInit for details). 1252 // partitionAllocGenericInit for details).
1148 if (!bucket->activePagesHead) 1253 if (!bucket->activePagesHead)
1149 bucketStats[i].isValid = false; 1254 bucketStats[i].isValid = false;
1150 else 1255 else
1151 partitionDumpBucketStats(&bucketStats[i], bucket); 1256 partitionDumpBucketStats(&bucketStats[i], bucket);
1152 } 1257 }
1153 1258
1154 static const size_t kMaxReportableDirectMaps = 4096;
1155 uint32_t directMapLengths[kMaxReportableDirectMaps];
1156 size_t numDirectMappedAllocations = 0;
1157 for (PartitionDirectMapExtent* extent = partition->directMapList; extent; ex tent = extent->nextExtent) { 1259 for (PartitionDirectMapExtent* extent = partition->directMapList; extent; ex tent = extent->nextExtent) {
1158 ASSERT(!extent->nextExtent || extent->nextExtent->prevExtent == extent); 1260 ASSERT(!extent->nextExtent || extent->nextExtent->prevExtent == extent);
1159 directMapLengths[numDirectMappedAllocations] = extent->bucket->slotSize; 1261 directMapLengths[numDirectMappedAllocations] = extent->bucket->slotSize;
1160 ++numDirectMappedAllocations; 1262 ++numDirectMappedAllocations;
1161 if (numDirectMappedAllocations == kMaxReportableDirectMaps) 1263 if (numDirectMappedAllocations == kMaxReportableDirectMaps)
1162 break; 1264 break;
1163 } 1265 }
1164 1266
1165 spinLockUnlock(&partition->lock); 1267 spinLockUnlock(&partition->lock);
1166 1268
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1199 // partitionsDumpBucketStats is called after collecting stats because it 1301 // partitionsDumpBucketStats is called after collecting stats because it
1200 // can use PartitionAlloc to allocate and this can affect the statistics. 1302 // can use PartitionAlloc to allocate and this can affect the statistics.
1201 for (size_t i = 0; i < partitionNumBuckets; ++i) { 1303 for (size_t i = 0; i < partitionNumBuckets; ++i) {
1202 if (memoryStats[i].isValid) 1304 if (memoryStats[i].isValid)
1203 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]); 1305 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]);
1204 } 1306 }
1205 } 1307 }
1206 1308
1207 } // namespace WTF 1309 } // namespace WTF
1208 1310
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