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

Side by Side Diff: third_party/tcmalloc/page_heap.cc

Issue 187008: Landing for Anton Muhin's tcmalloc patch:... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 3 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 | Annotate | Revision Log
« no previous file with comments | « third_party/tcmalloc/page_heap.h ('k') | third_party/tcmalloc/tcmalloc.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2008, Google Inc. 1 // Copyright (c) 2008, Google Inc.
2 // All rights reserved. 2 // 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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
44 "to return memory slower. Reasonable rates are in the " 44 "to return memory slower. Reasonable rates are in the "
45 "range [0,10]"); 45 "range [0,10]");
46 46
47 namespace tcmalloc { 47 namespace tcmalloc {
48 48
49 PageHeap::PageHeap() 49 PageHeap::PageHeap()
50 : pagemap_(MetaDataAlloc), 50 : pagemap_(MetaDataAlloc),
51 pagemap_cache_(0), 51 pagemap_cache_(0),
52 free_pages_(0), 52 free_pages_(0),
53 system_bytes_(0), 53 system_bytes_(0),
54 #if DEFER_DECOMMIT
55 free_committed_pages_(0),
56 pages_committed_since_last_scavenge_(0),
57 #else
54 scavenge_counter_(0), 58 scavenge_counter_(0),
59 #endif
55 // Start scavenging at kMaxPages list 60 // Start scavenging at kMaxPages list
56 scavenge_index_(kMaxPages-1) { 61 scavenge_index_(kMaxPages-1) {
57 COMPILE_ASSERT(kNumClasses <= (1 << PageMapCache::kValuebits), valuebits); 62 COMPILE_ASSERT(kNumClasses <= (1 << PageMapCache::kValuebits), valuebits);
58 DLL_Init(&large_.normal); 63 DLL_Init(&large_.normal);
59 DLL_Init(&large_.returned); 64 DLL_Init(&large_.returned);
60 for (int i = 0; i < kMaxPages; i++) { 65 for (int i = 0; i < kMaxPages; i++) {
61 DLL_Init(&free_[i].normal); 66 DLL_Init(&free_[i].normal);
62 DLL_Init(&free_[i].returned); 67 DLL_Init(&free_[i].returned);
63 } 68 }
64 } 69 }
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 Span* leftover = NewSpan(span->start + n, extra); 148 Span* leftover = NewSpan(span->start + n, extra);
144 ASSERT(leftover->location == Span::IN_USE); 149 ASSERT(leftover->location == Span::IN_USE);
145 Event(leftover, 'U', extra); 150 Event(leftover, 'U', extra);
146 RecordSpan(leftover); 151 RecordSpan(leftover);
147 pagemap_.set(span->start + n - 1, span); // Update map from pageid to span 152 pagemap_.set(span->start + n - 1, span); // Update map from pageid to span
148 span->length = n; 153 span->length = n;
149 154
150 return leftover; 155 return leftover;
151 } 156 }
152 157
158 void PageHeap::CommitSpan(Span* span) {
159 TCMalloc_SystemCommit(
160 reinterpret_cast<void*>(span->start << kPageShift),
161 static_cast<size_t>(span->length << kPageShift)
162 );
163 #if DEFER_DECOMMIT
164 pages_committed_since_last_scavenge_ += span->length;
165 #endif
166 }
167
153 Span* PageHeap::Carve(Span* span, Length n) { 168 Span* PageHeap::Carve(Span* span, Length n) {
154 ASSERT(n > 0); 169 ASSERT(n > 0);
155 ASSERT(span->location != Span::IN_USE); 170 ASSERT(span->location != Span::IN_USE);
156 const int old_location = span->location; 171 const int old_location = span->location;
157 DLL_Remove(span); 172 DLL_Remove(span);
158 span->location = Span::IN_USE; 173 span->location = Span::IN_USE;
159 Event(span, 'A', n); 174 Event(span, 'A', n);
160 175
161 const int extra = span->length - n; 176 const int extra = span->length - n;
162 ASSERT(extra >= 0); 177 ASSERT(extra >= 0);
163 if (extra > 0) { 178 if (extra > 0) {
164 Span* leftover = NewSpan(span->start + n, extra); 179 Span* leftover = NewSpan(span->start + n, extra);
165 leftover->location = old_location; 180 leftover->location = old_location;
166 Event(leftover, 'S', extra); 181 Event(leftover, 'S', extra);
167 RecordSpan(leftover); 182 RecordSpan(leftover);
168 183
169 // Place leftover span on appropriate free list 184 // Place leftover span on appropriate free list
170 SpanList* listpair = (extra < kMaxPages) ? &free_[extra] : &large_; 185 SpanList* listpair = (extra < kMaxPages) ? &free_[extra] : &large_;
171 Span* dst = (leftover->location == Span::ON_RETURNED_FREELIST 186 Span* dst = (leftover->location == Span::ON_RETURNED_FREELIST
172 ? &listpair->returned : &listpair->normal); 187 ? &listpair->returned : &listpair->normal);
173 DLL_Prepend(dst, leftover); 188 DLL_Prepend(dst, leftover);
174 189
175 span->length = n; 190 span->length = n;
176 pagemap_.set(span->start + n - 1, span); 191 pagemap_.set(span->start + n - 1, span);
177 } 192 }
193 if (old_location == Span::ON_RETURNED_FREELIST) {
194 // We need to recommit this address space.
195 CommitSpan(span);
196 } else {
197 #if DEFER_DECOMMIT
198 // The newly allocated memory is from a span that's already committed.
199 // Update the free_committed_pages_ count.
200 ASSERT(free_committed_pages_ >= n);
201 free_committed_pages_ -= n;
202 #endif
203 }
204 ASSERT(span->location == Span::IN_USE);
205 ASSERT(span->length == n);
178 ASSERT(Check()); 206 ASSERT(Check());
179 free_pages_ -= n; 207 free_pages_ -= n;
180 if (old_location == Span::ON_RETURNED_FREELIST) {
181 // We need to recommit this address space.
182 TCMalloc_SystemCommit(
183 reinterpret_cast<void*>(span->start << kPageShift),
184 static_cast<size_t>(span->length << kPageShift));
185 }
186 return span; 208 return span;
187 } 209 }
188 210
189 void PageHeap::Delete(Span* span) { 211 void PageHeap::Delete(Span* span) {
190 ASSERT(Check()); 212 ASSERT(Check());
191 ASSERT(span->location == Span::IN_USE); 213 ASSERT(span->location == Span::IN_USE);
192 ASSERT(span->length > 0); 214 ASSERT(span->length > 0);
193 ASSERT(GetDescriptor(span->start) == span); 215 ASSERT(GetDescriptor(span->start) == span);
194 ASSERT(GetDescriptor(span->start + span->length - 1) == span); 216 ASSERT(GetDescriptor(span->start + span->length - 1) == span);
195 span->sizeclass = 0; 217 span->sizeclass = 0;
196 span->sample = 0; 218 span->sample = 0;
197 219
198 // Coalesce -- we guarantee that "p" != 0, so no bounds checking 220 // Coalesce -- we guarantee that "p" != 0, so no bounds checking
199 // necessary. We do not bother resetting the stale pagemap 221 // necessary. We do not bother resetting the stale pagemap
200 // entries for the pieces we are merging together because we only 222 // entries for the pieces we are merging together because we only
201 // care about the pagemap entries for the boundaries. 223 // care about the pagemap entries for the boundaries.
202 // 224 //
203 // Note that the spans we merge into "span" may come out of 225 // Note that the spans we merge into "span" may come out of
204 // a "normal" list. For simplicity, we move these into the 226 // a "returned" list. We move those into "normal" list
205 // "returned" list of the appropriate size class. We do this 227 // as for 'immediate' operations we favour committing over
206 // so that we can maximize large, continuous blocks of freed 228 // decommitting (decommitting is performed offline).
207 // space.
208 const PageID p = span->start; 229 const PageID p = span->start;
209 const Length n = span->length; 230 const Length n = span->length;
210 Span* prev = GetDescriptor(p-1); 231 Span* prev = GetDescriptor(p-1);
211 if (prev != NULL && prev->location != Span::IN_USE) { 232 if (prev != NULL && prev->location != Span::IN_USE) {
212 // Merge preceding span into this span 233 // Merge preceding span into this span
213 ASSERT(prev->start + prev->length == p); 234 ASSERT(prev->start + prev->length == p);
214 const Length len = prev->length; 235 const Length len = prev->length;
236 if (prev->location == Span::ON_RETURNED_FREELIST) {
237 CommitSpan(prev);
238 #if DEFER_DECOMMIT
239 free_committed_pages_ += len;
240 #endif
241 }
215 DLL_Remove(prev); 242 DLL_Remove(prev);
216 DeleteSpan(prev); 243 DeleteSpan(prev);
217 span->start -= len; 244 span->start -= len;
218 span->length += len; 245 span->length += len;
219 pagemap_.set(span->start, span); 246 pagemap_.set(span->start, span);
220 Event(span, 'L', len); 247 Event(span, 'L', len);
221 } 248 }
222 Span* next = GetDescriptor(p+n); 249 Span* next = GetDescriptor(p+n);
223 if (next != NULL && next->location != Span::IN_USE) { 250 if (next != NULL && next->location != Span::IN_USE) {
224 // Merge next span into this span 251 // Merge next span into this span
225 ASSERT(next->start == p+n); 252 ASSERT(next->start == p+n);
226 const Length len = next->length; 253 const Length len = next->length;
254 if (next->location == Span::ON_RETURNED_FREELIST) {
255 CommitSpan(next);
256 #if DEFER_DECOMMIT
257 free_committed_pages_ += len;
258 #endif
259 }
227 DLL_Remove(next); 260 DLL_Remove(next);
228 DeleteSpan(next); 261 DeleteSpan(next);
229 span->length += len; 262 span->length += len;
230 pagemap_.set(span->start + span->length - 1, span); 263 pagemap_.set(span->start + span->length - 1, span);
231 Event(span, 'R', len); 264 Event(span, 'R', len);
232 } 265 }
233 266
234 Event(span, 'D', span->length); 267 Event(span, 'D', span->length);
235 span->location = Span::ON_RETURNED_FREELIST; 268 span->location = Span::ON_NORMAL_FREELIST;
236 TCMalloc_SystemRelease(reinterpret_cast<void*>(span->start << kPageShift),
237 static_cast<size_t>(span->length << kPageShift));
238 if (span->length < kMaxPages) 269 if (span->length < kMaxPages)
239 DLL_Prepend(&free_[span->length].returned, span); 270 DLL_Prepend(&free_[span->length].normal, span);
240 else 271 else
241 DLL_Prepend(&large_.returned, span); 272 DLL_Prepend(&large_.normal, span);
242 free_pages_ += n; 273 free_pages_ += n;
274 #if DEFER_DECOMMIT
275 free_committed_pages_ += n;
276 #endif
243 277
278 #if DEFER_DECOMMIT
279 // TODO(antonm): notify that could start scavenging
280 #else
244 IncrementalScavenge(n); 281 IncrementalScavenge(n);
282 #endif
245 ASSERT(Check()); 283 ASSERT(Check());
246 } 284 }
247 285
286
287 void PageHeap::Scavenge() {
288 #if DEFER_DECOMMIT
289 // If we have to commit memory since the last scavenge, it means we don't
290 // have enough free committed pages of necessary size for the amount of
291 // allocations that we do. So hold off on releasing memory back to the system .
292 if (pages_committed_since_last_scavenge_ > 0) {
293 pages_committed_since_last_scavenge_ = 0;
294 return;
295 }
296
297 if (free_committed_pages_ <= kMinimumFreeCommittedPageCount) {
298 return;
299 }
300
301 uint64_t to_decommit = std::min(
302 free_committed_pages_ - kMinimumFreeCommittedPageCount,
303 free_committed_pages_ / kMaxScavengeAmountFactor);
304 to_decommit = DecommitFromSpanList(&large_, to_decommit);
305 for (int i = kMaxPages - 1; i >= 0; i--) {
306 to_decommit = DecommitFromSpanList(&free_[i], to_decommit);
307 }
308
309 // Force at least one decommit from large list, otherwise big sized blocks
310 // sitting might block as from releasing smaller blocks behind.
311 if (to_decommit > 0) {
312 if (!DLL_IsEmpty(&large_.normal)) {
313 DecommitLastSpan(&large_, large_.normal.prev);
314 }
315 }
316 #endif
317 }
318
319 #if DEFER_DECOMMIT
320 Length PageHeap::DecommitLastSpan(SpanList* span_list, Span* span) {
321 ASSERT(!DLL_IsEmpty(&span_list->normal));
322 ASSERT(span_list->normal.prev == span);
323
324 Length length = span->length;
325
326 DLL_Remove(span);
327
328 TCMalloc_SystemRelease(reinterpret_cast<void*>(span->start << kPageShift), spa n->length << kPageShift);
329 span->location = Span::ON_RETURNED_FREELIST;
330 ASSERT(free_committed_pages_ >= length);
331 free_committed_pages_ -= length;
332
333 DLL_Prepend(&span_list->returned, span);
334
335 return length;
336 }
337
338 uint64_t PageHeap::DecommitFromSpanList(SpanList* span_list, uint64_t to_decommi t) {
339 while (!DLL_IsEmpty(&span_list->normal)) {
340 // Release the last span on the normal portion of this list.
341 Span* span = span_list->normal.prev;
342
343 if (span->length > to_decommit) {
344 return to_decommit;
345 }
346
347 to_decommit -= DecommitLastSpan(span_list, span);
348 }
349
350 return to_decommit;
351 }
352
353 #else
354
248 void PageHeap::IncrementalScavenge(Length n) { 355 void PageHeap::IncrementalScavenge(Length n) {
249 // Fast path; not yet time to release memory 356 // Fast path; not yet time to release memory
250 scavenge_counter_ -= n; 357 scavenge_counter_ -= n;
251 if (scavenge_counter_ >= 0) return; // Not yet time to scavenge 358 if (scavenge_counter_ >= 0) return; // Not yet time to scavenge
252 359
253 // Never delay scavenging for more than the following number of 360 // Never delay scavenging for more than the following number of
254 // deallocated pages. With 4K pages, this comes to 4GB of 361 // deallocated pages. With 4K pages, this comes to 4GB of
255 // deallocation. 362 // deallocation.
256 // Chrome: Changed to 64MB 363 // Chrome: Changed to 64MB
257 static const int kMaxReleaseDelay = 1 << 14; 364 static const int kMaxReleaseDelay = 1 << 14;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 scavenge_index_ = index; // Scavenge at index+1 next time 404 scavenge_index_ = index; // Scavenge at index+1 next time
298 // Note: we stop scavenging after finding one. 405 // Note: we stop scavenging after finding one.
299 return; 406 return;
300 } 407 }
301 index++; 408 index++;
302 } 409 }
303 410
304 // Nothing to scavenge, delay for a while 411 // Nothing to scavenge, delay for a while
305 scavenge_counter_ = kDefaultReleaseDelay; 412 scavenge_counter_ = kDefaultReleaseDelay;
306 } 413 }
414 #endif
307 415
308 void PageHeap::RegisterSizeClass(Span* span, size_t sc) { 416 void PageHeap::RegisterSizeClass(Span* span, size_t sc) {
309 // Associate span object with all interior pages as well 417 // Associate span object with all interior pages as well
310 ASSERT(span->location == Span::IN_USE); 418 ASSERT(span->location == Span::IN_USE);
311 ASSERT(GetDescriptor(span->start) == span); 419 ASSERT(GetDescriptor(span->start) == span);
312 ASSERT(GetDescriptor(span->start+span->length-1) == span); 420 ASSERT(GetDescriptor(span->start+span->length-1) == span);
313 Event(span, 'C', sc); 421 Event(span, 'C', sc);
314 span->sizeclass = sc; 422 span->sizeclass = sc;
315 for (Length i = 1; i < span->length-1; i++) { 423 for (Length i = 1; i < span->length-1; i++) {
316 pagemap_.set(span->start+i, span); 424 pagemap_.set(span->start+i, span);
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 void* ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize); 506 void* ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize);
399 if (ptr == NULL) { 507 if (ptr == NULL) {
400 if (n < ask) { 508 if (n < ask) {
401 // Try growing just "n" pages 509 // Try growing just "n" pages
402 ask = n; 510 ask = n;
403 ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize); 511 ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize);
404 } 512 }
405 if (ptr == NULL) return false; 513 if (ptr == NULL) return false;
406 } 514 }
407 ask = actual_size >> kPageShift; 515 ask = actual_size >> kPageShift;
516 #if DEFER_DECOMMIT
517 pages_committed_since_last_scavenge_ += ask;
518 #endif
408 RecordGrowth(ask << kPageShift); 519 RecordGrowth(ask << kPageShift);
409 520
410 uint64_t old_system_bytes = system_bytes_; 521 uint64_t old_system_bytes = system_bytes_;
411 system_bytes_ += (ask << kPageShift); 522 system_bytes_ += (ask << kPageShift);
412 const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; 523 const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
413 ASSERT(p > 0); 524 ASSERT(p > 0);
414 525
415 // If we have already a lot of pages allocated, just pre allocate a bunch of 526 // If we have already a lot of pages allocated, just pre allocate a bunch of
416 // memory for the page map. This prevents fragmentation by pagemap metadata 527 // memory for the page map. This prevents fragmentation by pagemap metadata
417 // when a program keeps allocating and freeing large blocks. 528 // when a program keeps allocating and freeing large blocks.
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
483 static_cast<size_t>(s->length << kPageShift)); 594 static_cast<size_t>(s->length << kPageShift));
484 } 595 }
485 } 596 }
486 597
487 void PageHeap::ReleaseFreePages() { 598 void PageHeap::ReleaseFreePages() {
488 for (Length s = 0; s < kMaxPages; s++) { 599 for (Length s = 0; s < kMaxPages; s++) {
489 ReleaseFreeList(&free_[s].normal, &free_[s].returned); 600 ReleaseFreeList(&free_[s].normal, &free_[s].returned);
490 } 601 }
491 ReleaseFreeList(&large_.normal, &large_.returned); 602 ReleaseFreeList(&large_.normal, &large_.returned);
492 ASSERT(Check()); 603 ASSERT(Check());
604 #if DEFER_DECOMMIT
605 free_committed_pages_ = 0;
606 #endif
493 } 607 }
494 608
495 } // namespace tcmalloc 609 } // namespace tcmalloc
OLDNEW
« no previous file with comments | « third_party/tcmalloc/page_heap.h ('k') | third_party/tcmalloc/tcmalloc.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698