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

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

Issue 1203893002: PartitionAlloc: make PartitionPurgeDiscardUnusedSystemPages discard more. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@discard
Patch Set: Compile fix. 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 | « no previous file | 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 1062 matching lines...) Expand 10 before | Expand all | Expand 10 after
1073 size_t copySize = actualOldSize; 1073 size_t copySize = actualOldSize;
1074 if (newSize < copySize) 1074 if (newSize < copySize)
1075 copySize = newSize; 1075 copySize = newSize;
1076 1076
1077 memcpy(ret, ptr, copySize); 1077 memcpy(ret, ptr, copySize);
1078 partitionFreeGeneric(root, ptr); 1078 partitionFreeGeneric(root, ptr);
1079 return ret; 1079 return ret;
1080 #endif 1080 #endif
1081 } 1081 }
1082 1082
1083 static size_t partitionPurgePage(const PartitionPage* page, bool discard) 1083 static size_t partitionPurgePage(PartitionPage* page, bool discard)
1084 { 1084 {
1085 const PartitionBucket* bucket = page->bucket; 1085 const PartitionBucket* bucket = page->bucket;
1086 if (bucket->slotSize < kSystemPageSize || !page->numAllocatedSlots) 1086 size_t slotSize = bucket->slotSize;
1087 if (slotSize < kSystemPageSize || !page->numAllocatedSlots)
1087 return 0; 1088 return 0;
1088 1089
1089 size_t bucketNumSlots = partitionBucketSlots(bucket); 1090 size_t bucketNumSlots = partitionBucketSlots(bucket);
1090 size_t discardableBytes = 0; 1091 size_t discardableBytes = 0;
1091 1092
1092 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); 1093 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page));
1093 if (rawSize) { 1094 if (rawSize) {
1094 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage( rawSize)); 1095 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage( rawSize));
1095 discardableBytes = bucket->slotSize - usedBytes; 1096 discardableBytes = bucket->slotSize - usedBytes;
1096 if (discardableBytes && discard) { 1097 if (discardableBytes && discard) {
1097 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); 1098 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page));
1098 ptr += usedBytes; 1099 ptr += usedBytes;
1099 discardSystemPages(ptr, discardableBytes); 1100 discardSystemPages(ptr, discardableBytes);
1100 } 1101 }
1101 return discardableBytes; 1102 return discardableBytes;
1102 } 1103 }
1103 1104
1104 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS pan) / kSystemPageSize; 1105 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS pan) / kSystemPageSize;
1105 ASSERT(bucketNumSlots <= maxSlotCount); 1106 ASSERT(bucketNumSlots <= maxSlotCount);
1107 ASSERT(page->numUnprovisionedSlots < bucketNumSlots);
1108 size_t numSlots = bucketNumSlots - page->numUnprovisionedSlots;
1106 char slotUsage[maxSlotCount]; 1109 char slotUsage[maxSlotCount];
1107 size_t lastSlot = static_cast<size_t>(-1); 1110 size_t lastSlot = static_cast<size_t>(-1);
1108 memset(slotUsage, 1, sizeof(slotUsage)); 1111 memset(slotUsage, 1, numSlots);
1109 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); 1112 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page));
1110 PartitionFreelistEntry* entry = page->freelistHead; 1113 PartitionFreelistEntry* entry = page->freelistHead;
1111 // First, walk the freelist for this page and make a bitmap of which slots 1114 // First, walk the freelist for this page and make a bitmap of which slots
1112 // are not in use. 1115 // are not in use.
1113 while (entry) { 1116 while (entry) {
1114 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / bucket->slot Size; 1117 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / slotSize;
1115 ASSERT(slotIndex < bucketNumSlots); 1118 ASSERT(slotIndex < numSlots);
1116 slotUsage[slotIndex] = 0; 1119 slotUsage[slotIndex] = 0;
1117 entry = partitionFreelistMask(entry->next); 1120 entry = partitionFreelistMask(entry->next);
1118 // If we have a slot where the masked freelist entry is 0, we can 1121 // If we have a slot where the masked freelist entry is 0, we can
1119 // actually discard that freelist entry because touching a discarded 1122 // actually discard that freelist entry because touching a discarded
1120 // page is guaranteed to return original content or 0. 1123 // page is guaranteed to return original content or 0.
1121 // (Note that this optimization won't fire on big endian machines 1124 // (Note that this optimization won't fire on big endian machines
1122 // because the masking function is negation.) 1125 // because the masking function is negation.)
1123 if (!partitionFreelistMask(entry)) 1126 if (!partitionFreelistMask(entry))
1124 lastSlot = slotIndex; 1127 lastSlot = slotIndex;
1125 } 1128 }
1129
1130 // If the slot(s) at the end of the slot span are not in used, we can
1131 // truncate them entirely and rewrite the freelist.
1132 size_t truncatedSlots = 0;
1133 while (!slotUsage[numSlots - 1]) {
1134 truncatedSlots++;
1135 numSlots--;
1136 ASSERT(numSlots);
1137 }
1138 // First, do the work of calculating the discardable bytes. Don't actually
1139 // discard anything unless the discard flag was passed in.
1140 char* beginPtr = nullptr;
1141 char* endPtr = nullptr;
1142 size_t unprovisionedBytes = 0;
1143 if (truncatedSlots) {
1144 beginPtr = ptr + (numSlots * slotSize);
1145 endPtr = beginPtr + (slotSize * truncatedSlots);
1146 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr)));
1147 // We round the end pointer here up and not down because we're at the
1148 // end of a slot span, so we "own" all the way up the page boundary.
1149 endPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterpre t_cast<size_t>(endPtr)));
1150 ASSERT(endPtr <= ptr + partitionBucketBytes(bucket));
1151 if (beginPtr < endPtr) {
1152 unprovisionedBytes = endPtr - beginPtr;
1153 discardableBytes += unprovisionedBytes;
1154 }
1155 }
1156 if (unprovisionedBytes && discard) {
1157 ASSERT(truncatedSlots > 0);
1158 size_t numNewEntries = 0;
1159 page->numUnprovisionedSlots += truncatedSlots;
1160 // Rewrite the freelist.
1161 PartitionFreelistEntry** entryPtr = &page->freelistHead;
1162 for (size_t slotIndex = 0; slotIndex < numSlots; ++slotIndex) {
1163 if (slotUsage[slotIndex])
1164 continue;
1165 PartitionFreelistEntry* entry = reinterpret_cast<PartitionFreelistEn try*>(ptr + (slotSize * slotIndex));
1166 *entryPtr = partitionFreelistMask(entry);
1167 entryPtr = reinterpret_cast<PartitionFreelistEntry**>(entry);
1168 numNewEntries++;
1169 }
1170 // Terminate the freelist chain.
1171 *entryPtr = nullptr;
1172 // The freelist head is stored unmasked.
1173 page->freelistHead = partitionFreelistMask(page->freelistHead);
1174 ASSERT(numNewEntries == numSlots - page->numAllocatedSlots);
1175 // Discard the memory.
1176 discardSystemPages(beginPtr, unprovisionedBytes);
1177 }
1178
1126 // Next, walk the slots and for any not in use, consider where the system 1179 // Next, walk the slots and for any not in use, consider where the system
1127 // page boundaries occur. We can release any system pages back to the 1180 // page boundaries occur. We can release any system pages back to the
1128 // system as long as we don't interfere with a freelist pointer or an 1181 // system as long as we don't interfere with a freelist pointer or an
1129 // adjacent slot. 1182 // adjacent slot.
1130 // TODO(cevans): I think we can "truncate" the page, i.e. increase the 1183 for (size_t i = 0; i < numSlots; ++i) {
1131 // value of page->numUnprovisionedSlots and rewrite(!) the freelist, if
1132 // we find that to be a win too.
1133 for (size_t i = 0; i < bucketNumSlots; ++i) {
1134 if (slotUsage[i]) 1184 if (slotUsage[i])
1135 continue; 1185 continue;
1136 // The first address we can safely discard is just after the freelist 1186 // The first address we can safely discard is just after the freelist
1137 // pointer. There's one quirk: if the freelist pointer is actually a 1187 // pointer. There's one quirk: if the freelist pointer is actually a
1138 // null, we can discard that pointer value too. 1188 // null, we can discard that pointer value too.
1139 char* beginPtr = ptr + (i * bucket->slotSize); 1189 char* beginPtr = ptr + (i * slotSize);
1140 char* endPtr = beginPtr + bucket->slotSize; 1190 char* endPtr = beginPtr + slotSize;
1141 if (i != lastSlot) 1191 if (i != lastSlot)
1142 beginPtr += sizeof(PartitionFreelistEntry); 1192 beginPtr += sizeof(PartitionFreelistEntry);
1143 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr))); 1193 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr)));
1144 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp ret_cast<size_t>(endPtr))); 1194 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp ret_cast<size_t>(endPtr)));
1145 if (beginPtr < endPtr) { 1195 if (beginPtr < endPtr) {
1146 size_t partialSlotBytes = endPtr - beginPtr; 1196 size_t partialSlotBytes = endPtr - beginPtr;
1147 discardableBytes += partialSlotBytes; 1197 discardableBytes += partialSlotBytes;
1148 if (discard) 1198 if (discard)
1149 discardSystemPages(beginPtr, partialSlotBytes); 1199 discardSystemPages(beginPtr, partialSlotBytes);
1150 } 1200 }
1151 } 1201 }
1152 return discardableBytes; 1202 return discardableBytes;
1153 } 1203 }
1154 1204
1155 static void partitionPurgeBucket(const PartitionBucket* bucket) 1205 static void partitionPurgeBucket(PartitionBucket* bucket)
1156 { 1206 {
1157 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { 1207 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) {
1158 for (const PartitionPage* page = bucket->activePagesHead; page; page = p age->nextPage) { 1208 for (PartitionPage* page = bucket->activePagesHead; page; page = page->n extPage) {
1159 ASSERT(page != &PartitionRootGeneric::gSeedPage); 1209 ASSERT(page != &PartitionRootGeneric::gSeedPage);
1160 (void) partitionPurgePage(page, true); 1210 (void) partitionPurgePage(page, true);
1161 } 1211 }
1162 } 1212 }
1163 } 1213 }
1164 1214
1165 void partitionPurgeMemory(PartitionRoot* root, int flags) 1215 void partitionPurgeMemory(PartitionRoot* root, int flags)
1166 { 1216 {
1167 if (flags & PartitionPurgeDecommitEmptyPages) 1217 if (flags & PartitionPurgeDecommitEmptyPages)
1168 partitionDecommitEmptyPages(root); 1218 partitionDecommitEmptyPages(root);
1169 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages 1219 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages
1170 // here because that flag is only useful for allocations >= system page 1220 // here because that flag is only useful for allocations >= system page
1171 // size. We only have allocations that large inside generic partitions 1221 // size. We only have allocations that large inside generic partitions
1172 // at the moment. 1222 // at the moment.
1173 } 1223 }
1174 1224
1175 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) 1225 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags)
1176 { 1226 {
1177 spinLockLock(&root->lock); 1227 spinLockLock(&root->lock);
1178 if (flags & PartitionPurgeDecommitEmptyPages) 1228 if (flags & PartitionPurgeDecommitEmptyPages)
1179 partitionDecommitEmptyPages(root); 1229 partitionDecommitEmptyPages(root);
1180 if (flags & PartitionPurgeDiscardUnusedSystemPages) { 1230 if (flags & PartitionPurgeDiscardUnusedSystemPages) {
1181 for (size_t i = 0; i < kGenericNumBuckets; ++i) { 1231 for (size_t i = 0; i < kGenericNumBuckets; ++i) {
1182 const PartitionBucket* bucket = &root->buckets[i]; 1232 PartitionBucket* bucket = &root->buckets[i];
1183 if (bucket->slotSize >= kSystemPageSize) 1233 if (bucket->slotSize >= kSystemPageSize)
1184 partitionPurgeBucket(bucket); 1234 partitionPurgeBucket(bucket);
1185 } 1235 }
1186 } 1236 }
1187 spinLockUnlock(&root->lock); 1237 spinLockUnlock(&root->lock);
1188 } 1238 }
1189 1239
1190 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page) 1240 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page)
1191 { 1241 {
1192 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); 1242 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket);
1193 1243
1194 if (partitionPageStateIsDecommitted(page)) { 1244 if (partitionPageStateIsDecommitted(page)) {
1195 ++statsOut->numDecommittedPages; 1245 ++statsOut->numDecommittedPages;
1196 return; 1246 return;
1197 } 1247 }
1198 1248
1199 statsOut->discardableBytes += partitionPurgePage(page, false); 1249 statsOut->discardableBytes += partitionPurgePage(const_cast<PartitionPage*>( page), false);
1200 1250
1201 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); 1251 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page));
1202 if (rawSize) 1252 if (rawSize)
1203 statsOut->activeBytes += static_cast<uint32_t>(rawSize); 1253 statsOut->activeBytes += static_cast<uint32_t>(rawSize);
1204 else 1254 else
1205 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot Size); 1255 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot Size);
1206 1256
1207 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa ge->numUnprovisionedSlots) * statsOut->bucketSlotSize); 1257 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa ge->numUnprovisionedSlots) * statsOut->bucketSlotSize);
1208 statsOut->residentBytes += pageBytesResident; 1258 statsOut->residentBytes += pageBytesResident;
1209 if (partitionPageStateIsEmpty(page)) { 1259 if (partitionPageStateIsEmpty(page)) {
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
1320 // partitionsDumpBucketStats is called after collecting stats because it 1370 // partitionsDumpBucketStats is called after collecting stats because it
1321 // can use PartitionAlloc to allocate and this can affect the statistics. 1371 // can use PartitionAlloc to allocate and this can affect the statistics.
1322 for (size_t i = 0; i < partitionNumBuckets; ++i) { 1372 for (size_t i = 0; i < partitionNumBuckets; ++i) {
1323 if (memoryStats[i].isValid) 1373 if (memoryStats[i].isValid)
1324 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]); 1374 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]);
1325 } 1375 }
1326 } 1376 }
1327 1377
1328 } // namespace WTF 1378 } // namespace WTF
1329 1379
OLDNEW
« no previous file with comments | « no previous file | Source/wtf/PartitionAllocTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698