 Chromium Code Reviews
 Chromium Code Reviews Issue 7671034:
  doubly-linked free-lists for thread caches and page heaps  (Closed) 
  Base URL: http://git.chromium.org/git/chromium.git@trunk
    
  
    Issue 7671034:
  doubly-linked free-lists for thread caches and page heaps  (Closed) 
  Base URL: http://git.chromium.org/git/chromium.git@trunk| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2008, Google Inc. | 1 // Copyright (c) 2011, 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 | 
| 11 // copyright notice, this list of conditions and the following disclaimer | 11 // copyright notice, this list of conditions and the following disclaimer | 
| (...skipping 13 matching lines...) Expand all Loading... | |
| 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| 29 | 29 | 
| 30 // --- | 30 // --- | 
| 31 // Author: Sanjay Ghemawat <opensource@google.com> | 31 // Author: Sanjay Ghemawat <opensource@google.com> | 
| 32 | 32 | 
| 33 #include "config.h" | 33 #include "config.h" | 
| 34 #include "central_freelist.h" | 34 #include "central_freelist.h" | 
| 35 | 35 #include "free_list.h" // for FL_Next, FL_Push, etc | 
| 36 #include "internal_logging.h" // for ASSERT, MESSAGE | 36 #include "internal_logging.h" // for ASSERT, MESSAGE | 
| 37 #include "linked_list.h" // for SLL_Next, SLL_Push, etc | |
| 38 #include "page_heap.h" // for PageHeap | 37 #include "page_heap.h" // for PageHeap | 
| 39 #include "static_vars.h" // for Static | 38 #include "static_vars.h" // for Static | 
| 40 | 39 | 
| 41 namespace tcmalloc { | 40 namespace tcmalloc { | 
| 42 | 41 | 
| 43 void CentralFreeList::Init(size_t cl) { | 42 void CentralFreeList::Init(size_t cl) { | 
| 44 size_class_ = cl; | 43 size_class_ = cl; | 
| 45 tcmalloc::DLL_Init(&empty_); | 44 tcmalloc::DLL_Init(&empty_); | 
| 46 tcmalloc::DLL_Init(&nonempty_); | 45 tcmalloc::DLL_Init(&nonempty_); | 
| 47 counter_ = 0; | 46 counter_ = 0; | 
| 48 | 47 | 
| 49 #ifdef TCMALLOC_SMALL_BUT_SLOW | 48 #ifdef TCMALLOC_SMALL_BUT_SLOW | 
| 50 // Disable the transfer cache for the small footprint case. | 49 // Disable the transfer cache for the small footprint case. | 
| 51 cache_size_ = 0; | 50 cache_size_ = 0; | 
| 52 #else | 51 #else | 
| 53 cache_size_ = 16; | 52 cache_size_ = 16; | 
| 54 #endif | 53 #endif | 
| 55 used_slots_ = 0; | 54 used_slots_ = 0; | 
| 56 ASSERT(cache_size_ <= kNumTransferEntries); | 55 ASSERT(cache_size_ <= kNumTransferEntries); | 
| 57 } | 56 } | 
| 58 | 57 | 
| 59 void CentralFreeList::ReleaseListToSpans(void* start) { | 58 void CentralFreeList::ReleaseListToSpans(void* start) { | 
| 60 while (start) { | 59 while (start) { | 
| 61 void *next = SLL_Next(start); | 60 void *next = FL_Next(start); | 
| 62 ReleaseToSpans(start); | 61 ReleaseToSpans(start); | 
| 63 start = next; | 62 start = next; | 
| 64 } | 63 } | 
| 65 } | 64 } | 
| 66 | 65 | 
| 67 // MapObjectToSpan should logically be part of ReleaseToSpans. But | 66 // MapObjectToSpan should logically be part of ReleaseToSpans. But | 
| 68 // this triggers an optimization bug in gcc 4.5.0. Moving to a | 67 // this triggers an optimization bug in gcc 4.5.0. Moving to a | 
| 69 // separate function, and making sure that function isn't inlined, | 68 // separate function, and making sure that function isn't inlined, | 
| 70 // seems to fix the problem. It also should be fixed for gcc 4.5.1. | 69 // seems to fix the problem. It also should be fixed for gcc 4.5.1. | 
| 71 static | 70 static | 
| (...skipping 14 matching lines...) Expand all Loading... | |
| 86 // If span is empty, move it to non-empty list | 85 // If span is empty, move it to non-empty list | 
| 87 if (span->objects == NULL) { | 86 if (span->objects == NULL) { | 
| 88 tcmalloc::DLL_Remove(span); | 87 tcmalloc::DLL_Remove(span); | 
| 89 tcmalloc::DLL_Prepend(&nonempty_, span); | 88 tcmalloc::DLL_Prepend(&nonempty_, span); | 
| 90 Event(span, 'N', 0); | 89 Event(span, 'N', 0); | 
| 91 } | 90 } | 
| 92 | 91 | 
| 93 // The following check is expensive, so it is disabled by default | 92 // The following check is expensive, so it is disabled by default | 
| 94 if (false) { | 93 if (false) { | 
| 95 // Check that object does not occur in list | 94 // Check that object does not occur in list | 
| 95 void *next = span->objects; | |
| 
jschuh
2011/08/24 17:22:30
It looks like you left some debugging code behind.
 
bxx
2011/08/24 22:02:09
Good catch.  I don't even remember needing to debu
 | |
| 96 int got = 0; | 96 int got = 0; | 
| 97 for (void* p = span->objects; p != NULL; p = *((void**) p)) { | 97 for (void* p = next; p != NULL; p = FL_Next(p)){ | 
| 98 ASSERT(p != object); | 98 ASSERT(p != object); | 
| 99 got++; | 99 got++; | 
| 100 } | 100 } | 
| 101 ASSERT(got + span->refcount == | 101 ASSERT(got + span->refcount == | 
| 102 (span->length<<kPageShift) / | 102 (span->length<<kPageShift) / | 
| 103 Static::sizemap()->ByteSizeForClass(span->sizeclass)); | 103 Static::sizemap()->ByteSizeForClass(span->sizeclass)); | 
| 104 } | 104 } | 
| 105 | 105 | 
| 106 counter_++; | 106 counter_++; | 
| 107 span->refcount--; | 107 span->refcount--; | 
| 108 if (span->refcount == 0) { | 108 if (span->refcount == 0) { | 
| 109 Event(span, '#', 0); | 109 Event(span, '#', 0); | 
| 110 counter_ -= ((span->length<<kPageShift) / | 110 counter_ -= ((span->length<<kPageShift) / | 
| 111 Static::sizemap()->ByteSizeForClass(span->sizeclass)); | 111 Static::sizemap()->ByteSizeForClass(span->sizeclass)); | 
| 112 tcmalloc::DLL_Remove(span); | 112 tcmalloc::DLL_Remove(span); | 
| 113 | 113 | 
| 114 // Release central list lock while operating on pageheap | 114 // Release central list lock while operating on pageheap | 
| 115 lock_.Unlock(); | 115 lock_.Unlock(); | 
| 116 { | 116 { | 
| 117 SpinLockHolder h(Static::pageheap_lock()); | 117 SpinLockHolder h(Static::pageheap_lock()); | 
| 118 Static::pageheap()->Delete(span); | 118 Static::pageheap()->Delete(span); | 
| 119 } | 119 } | 
| 120 lock_.Lock(); | 120 lock_.Lock(); | 
| 121 } else { | 121 } else { | 
| 122 *(reinterpret_cast<void**>(object)) = span->objects; | 122 FL_Push(&(span->objects), object); | 
| 123 span->objects = object; | |
| 124 } | 123 } | 
| 125 } | 124 } | 
| 126 | 125 | 
| 127 bool CentralFreeList::EvictRandomSizeClass( | 126 bool CentralFreeList::EvictRandomSizeClass( | 
| 128 int locked_size_class, bool force) { | 127 int locked_size_class, bool force) { | 
| 129 static int race_counter = 0; | 128 static int race_counter = 0; | 
| 130 int t = race_counter++; // Updated without a lock, but who cares. | 129 int t = race_counter++; // Updated without a lock, but who cares. | 
| 131 if (t >= kNumClasses) { | 130 if (t >= kNumClasses) { | 
| 132 while (t >= kNumClasses) { | 131 while (t >= kNumClasses) { | 
| 133 t -= kNumClasses; | 132 t -= kNumClasses; | 
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 232 lock_.Unlock(); | 231 lock_.Unlock(); | 
| 233 return N; | 232 return N; | 
| 234 } | 233 } | 
| 235 | 234 | 
| 236 int result = 0; | 235 int result = 0; | 
| 237 void* head = NULL; | 236 void* head = NULL; | 
| 238 void* tail = NULL; | 237 void* tail = NULL; | 
| 239 // TODO: Prefetch multiple TCEntries? | 238 // TODO: Prefetch multiple TCEntries? | 
| 240 tail = FetchFromSpansSafe(); | 239 tail = FetchFromSpansSafe(); | 
| 241 if (tail != NULL) { | 240 if (tail != NULL) { | 
| 242 SLL_SetNext(tail, NULL); | 241 FL_Push(&head, tail); | 
| 243 head = tail; | |
| 244 result = 1; | 242 result = 1; | 
| 245 while (result < N) { | 243 while (result < N) { | 
| 246 void *t = FetchFromSpans(); | 244 void *t = FetchFromSpans(); | 
| 247 if (!t) break; | 245 if (!t) break; | 
| 248 SLL_Push(&head, t); | 246 FL_Push(&head, t); | 
| 249 result++; | 247 result++; | 
| 250 } | 248 } | 
| 251 } | 249 } | 
| 252 lock_.Unlock(); | 250 lock_.Unlock(); | 
| 253 *start = head; | 251 *start = head; | 
| 254 *end = tail; | 252 *end = tail; | 
| 255 return result; | 253 return result; | 
| 256 } | 254 } | 
| 257 | 255 | 
| 258 | 256 | 
| 259 void* CentralFreeList::FetchFromSpansSafe() { | 257 void* CentralFreeList::FetchFromSpansSafe() { | 
| 260 void *t = FetchFromSpans(); | 258 void *t = FetchFromSpans(); | 
| 261 if (!t) { | 259 if (!t) { | 
| 262 Populate(); | 260 Populate(); | 
| 263 t = FetchFromSpans(); | 261 t = FetchFromSpans(); | 
| 264 } | 262 } | 
| 265 return t; | 263 return t; | 
| 266 } | 264 } | 
| 267 | 265 | 
| 268 void* CentralFreeList::FetchFromSpans() { | 266 void* CentralFreeList::FetchFromSpans() { | 
| 269 if (tcmalloc::DLL_IsEmpty(&nonempty_)) return NULL; | 267 if (tcmalloc::DLL_IsEmpty(&nonempty_)) return NULL; | 
| 270 Span* span = nonempty_.next; | 268 Span* span = nonempty_.next; | 
| 271 | 269 | 
| 272 ASSERT(span->objects != NULL); | 270 ASSERT(span->objects != NULL); | 
| 273 span->refcount++; | 271 span->refcount++; | 
| 274 void* result = span->objects; | 272 void *result = FL_Pop(&(span->objects)); | 
| 275 span->objects = *(reinterpret_cast<void**>(result)); | |
| 276 if (span->objects == NULL) { | 273 if (span->objects == NULL) { | 
| 277 // Move to empty list | 274 // Move to empty list | 
| 278 tcmalloc::DLL_Remove(span); | 275 tcmalloc::DLL_Remove(span); | 
| 279 tcmalloc::DLL_Prepend(&empty_, span); | 276 tcmalloc::DLL_Prepend(&empty_, span); | 
| 280 Event(span, 'E', 0); | 277 Event(span, 'E', 0); | 
| 281 } | 278 } | 
| 282 counter_--; | 279 counter_--; | 
| 283 return result; | 280 return result; | 
| 284 } | 281 } | 
| 285 | 282 | 
| (...skipping 17 matching lines...) Expand all Loading... | |
| 303 ASSERT(span->length == npages); | 300 ASSERT(span->length == npages); | 
| 304 // Cache sizeclass info eagerly. Locking is not necessary. | 301 // Cache sizeclass info eagerly. Locking is not necessary. | 
| 305 // (Instead of being eager, we could just replace any stale info | 302 // (Instead of being eager, we could just replace any stale info | 
| 306 // about this span, but that seems to be no better in practice.) | 303 // about this span, but that seems to be no better in practice.) | 
| 307 for (int i = 0; i < npages; i++) { | 304 for (int i = 0; i < npages; i++) { | 
| 308 Static::pageheap()->CacheSizeClass(span->start + i, size_class_); | 305 Static::pageheap()->CacheSizeClass(span->start + i, size_class_); | 
| 309 } | 306 } | 
| 310 | 307 | 
| 311 // Split the block into pieces and add to the free-list | 308 // Split the block into pieces and add to the free-list | 
| 312 // TODO: coloring of objects to avoid cache conflicts? | 309 // TODO: coloring of objects to avoid cache conflicts? | 
| 313 void** tail = &span->objects; | 310 void* list = NULL; | 
| 314 char* ptr = reinterpret_cast<char*>(span->start << kPageShift); | 311 char* ptr = reinterpret_cast<char*>(span->start << kPageShift); | 
| 315 char* limit = ptr + (npages << kPageShift); | 312 char* limit = ptr + (npages << kPageShift); | 
| 316 const size_t size = Static::sizemap()->ByteSizeForClass(size_class_); | 313 const size_t size = Static::sizemap()->ByteSizeForClass(size_class_); | 
| 317 int num = 0; | 314 int num = 0; | 
| 318 while (ptr + size <= limit) { | 315 while (ptr + size <= limit) { | 
| 319 *tail = ptr; | 316 FL_Push(&list, reinterpret_cast<void*>(ptr)); | 
| 
jschuh
2011/08/24 17:22:30
Shouldn't need the reinterpret cast here.
 
bxx
2011/08/24 22:02:09
Done.   I assumed I needed it because ptr is of ty
 | |
| 320 tail = reinterpret_cast<void**>(ptr); | |
| 321 ptr += size; | 317 ptr += size; | 
| 322 num++; | 318 num++; | 
| 323 } | 319 } | 
| 324 ASSERT(ptr <= limit); | 320 ASSERT(ptr <= limit); | 
| 325 *tail = NULL; | |
| 326 span->refcount = 0; // No sub-object in use yet | 321 span->refcount = 0; // No sub-object in use yet | 
| 322 span->objects = list; | |
| 327 | 323 | 
| 328 // Add span to list of non-empty spans | 324 // Add span to list of non-empty spans | 
| 329 lock_.Lock(); | 325 lock_.Lock(); | 
| 330 tcmalloc::DLL_Prepend(&nonempty_, span); | 326 tcmalloc::DLL_Prepend(&nonempty_, span); | 
| 331 counter_ += num; | 327 counter_ += num; | 
| 332 } | 328 } | 
| 333 | 329 | 
| 334 int CentralFreeList::tc_length() { | 330 int CentralFreeList::tc_length() { | 
| 335 SpinLockHolder h(&lock_); | 331 SpinLockHolder h(&lock_); | 
| 336 return used_slots_ * Static::sizemap()->num_objects_to_move(size_class_); | 332 return used_slots_ * Static::sizemap()->num_objects_to_move(size_class_); | 
| 337 } | 333 } | 
| 338 | 334 | 
| 339 } // namespace tcmalloc | 335 } // namespace tcmalloc | 
| OLD | NEW |