OLD | NEW |
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 13 matching lines...) Expand all Loading... |
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
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 <algorithm> |
34 #include "central_freelist.h" | 35 #include "central_freelist.h" |
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 |
37 #include "page_heap.h" // for PageHeap | 38 #include "page_heap.h" // for PageHeap |
38 #include "static_vars.h" // for Static | 39 #include "static_vars.h" // for Static |
39 | 40 |
| 41 using std::min; |
| 42 using std::max; |
| 43 |
40 namespace tcmalloc { | 44 namespace tcmalloc { |
41 | 45 |
42 void CentralFreeList::Init(size_t cl) { | 46 void CentralFreeList::Init(size_t cl) { |
43 size_class_ = cl; | 47 size_class_ = cl; |
44 tcmalloc::DLL_Init(&empty_); | 48 tcmalloc::DLL_Init(&empty_); |
45 tcmalloc::DLL_Init(&nonempty_); | 49 tcmalloc::DLL_Init(&nonempty_); |
| 50 num_spans_ = 0; |
46 counter_ = 0; | 51 counter_ = 0; |
47 | 52 |
| 53 max_cache_size_ = kMaxNumTransferEntries; |
48 #ifdef TCMALLOC_SMALL_BUT_SLOW | 54 #ifdef TCMALLOC_SMALL_BUT_SLOW |
49 // Disable the transfer cache for the small footprint case. | 55 // Disable the transfer cache for the small footprint case. |
50 cache_size_ = 0; | 56 cache_size_ = 0; |
51 #else | 57 #else |
52 cache_size_ = 16; | 58 cache_size_ = 16; |
53 #endif | 59 #endif |
| 60 if (cl > 0) { |
| 61 // Limit the maximum size of the cache based on the size class. If this |
| 62 // is not done, large size class objects will consume a lot of memory if |
| 63 // they just sit in the transfer cache. |
| 64 int32_t bytes = Static::sizemap()->ByteSizeForClass(cl); |
| 65 int32_t objs_to_move = Static::sizemap()->num_objects_to_move(cl); |
| 66 |
| 67 ASSERT(objs_to_move > 0 && bytes > 0); |
| 68 // Limit each size class cache to at most 1MB of objects or one entry, |
| 69 // whichever is greater. Total transfer cache memory used across all |
| 70 // size classes then can't be greater than approximately |
| 71 // 1MB * kMaxNumTransferEntries. |
| 72 // min and max are in parens to avoid macro-expansion on windows. |
| 73 max_cache_size_ = (min)(max_cache_size_, |
| 74 (max)(1, (1024 * 1024) / (bytes * objs_to_move))); |
| 75 cache_size_ = (min)(cache_size_, max_cache_size_); |
| 76 } |
54 used_slots_ = 0; | 77 used_slots_ = 0; |
55 ASSERT(cache_size_ <= kNumTransferEntries); | 78 ASSERT(cache_size_ <= max_cache_size_); |
56 } | 79 } |
57 | 80 |
58 void CentralFreeList::ReleaseListToSpans(void* start) { | 81 void CentralFreeList::ReleaseListToSpans(void* start) { |
59 while (start) { | 82 while (start) { |
60 void *next = FL_Next(start); | 83 void *next = SLL_Next(start); |
61 ReleaseToSpans(start); | 84 ReleaseToSpans(start); |
62 start = next; | 85 start = next; |
63 } | 86 } |
64 } | 87 } |
65 | 88 |
66 // MapObjectToSpan should logically be part of ReleaseToSpans. But | 89 // MapObjectToSpan should logically be part of ReleaseToSpans. But |
67 // this triggers an optimization bug in gcc 4.5.0. Moving to a | 90 // this triggers an optimization bug in gcc 4.5.0. Moving to a |
68 // separate function, and making sure that function isn't inlined, | 91 // separate function, and making sure that function isn't inlined, |
69 // seems to fix the problem. It also should be fixed for gcc 4.5.1. | 92 // seems to fix the problem. It also should be fixed for gcc 4.5.1. |
70 static | 93 static |
(...skipping 15 matching lines...) Expand all Loading... |
86 if (span->objects == NULL) { | 109 if (span->objects == NULL) { |
87 tcmalloc::DLL_Remove(span); | 110 tcmalloc::DLL_Remove(span); |
88 tcmalloc::DLL_Prepend(&nonempty_, span); | 111 tcmalloc::DLL_Prepend(&nonempty_, span); |
89 Event(span, 'N', 0); | 112 Event(span, 'N', 0); |
90 } | 113 } |
91 | 114 |
92 // The following check is expensive, so it is disabled by default | 115 // The following check is expensive, so it is disabled by default |
93 if (false) { | 116 if (false) { |
94 // Check that object does not occur in list | 117 // Check that object does not occur in list |
95 int got = 0; | 118 int got = 0; |
96 for (void* p = span->objects; p != NULL; p = FL_Next(p)){ | 119 for (void* p = span->objects; p != NULL; p = *((void**) p)) { |
97 ASSERT(p != object); | 120 ASSERT(p != object); |
98 got++; | 121 got++; |
99 } | 122 } |
100 ASSERT(got + span->refcount == | 123 ASSERT(got + span->refcount == |
101 (span->length<<kPageShift) / | 124 (span->length<<kPageShift) / |
102 Static::sizemap()->ByteSizeForClass(span->sizeclass)); | 125 Static::sizemap()->ByteSizeForClass(span->sizeclass)); |
103 } | 126 } |
104 | 127 |
105 counter_++; | 128 counter_++; |
106 span->refcount--; | 129 span->refcount--; |
107 if (span->refcount == 0) { | 130 if (span->refcount == 0) { |
108 Event(span, '#', 0); | 131 Event(span, '#', 0); |
109 counter_ -= ((span->length<<kPageShift) / | 132 counter_ -= ((span->length<<kPageShift) / |
110 Static::sizemap()->ByteSizeForClass(span->sizeclass)); | 133 Static::sizemap()->ByteSizeForClass(span->sizeclass)); |
111 tcmalloc::DLL_Remove(span); | 134 tcmalloc::DLL_Remove(span); |
| 135 --num_spans_; |
112 | 136 |
113 // Release central list lock while operating on pageheap | 137 // Release central list lock while operating on pageheap |
114 lock_.Unlock(); | 138 lock_.Unlock(); |
115 { | 139 { |
116 SpinLockHolder h(Static::pageheap_lock()); | 140 SpinLockHolder h(Static::pageheap_lock()); |
117 Static::pageheap()->Delete(span); | 141 Static::pageheap()->Delete(span); |
118 } | 142 } |
119 lock_.Lock(); | 143 lock_.Lock(); |
120 } else { | 144 } else { |
121 FL_Push(&(span->objects), object); | 145 *(reinterpret_cast<void**>(object)) = span->objects; |
| 146 span->objects = object; |
122 } | 147 } |
123 } | 148 } |
124 | 149 |
125 bool CentralFreeList::EvictRandomSizeClass( | 150 bool CentralFreeList::EvictRandomSizeClass( |
126 int locked_size_class, bool force) { | 151 int locked_size_class, bool force) { |
127 static int race_counter = 0; | 152 static int race_counter = 0; |
128 int t = race_counter++; // Updated without a lock, but who cares. | 153 int t = race_counter++; // Updated without a lock, but who cares. |
129 if (t >= kNumClasses) { | 154 if (t >= kNumClasses) { |
130 while (t >= kNumClasses) { | 155 while (t >= kNumClasses) { |
131 t -= kNumClasses; | 156 t -= kNumClasses; |
132 } | 157 } |
133 race_counter = t; | 158 race_counter = t; |
134 } | 159 } |
135 ASSERT(t >= 0); | 160 ASSERT(t >= 0); |
136 ASSERT(t < kNumClasses); | 161 ASSERT(t < kNumClasses); |
137 if (t == locked_size_class) return false; | 162 if (t == locked_size_class) return false; |
138 return Static::central_cache()[t].ShrinkCache(locked_size_class, force); | 163 return Static::central_cache()[t].ShrinkCache(locked_size_class, force); |
139 } | 164 } |
140 | 165 |
141 bool CentralFreeList::MakeCacheSpace() { | 166 bool CentralFreeList::MakeCacheSpace() { |
142 // Is there room in the cache? | 167 // Is there room in the cache? |
143 if (used_slots_ < cache_size_) return true; | 168 if (used_slots_ < cache_size_) return true; |
144 // Check if we can expand this cache? | 169 // Check if we can expand this cache? |
145 if (cache_size_ == kNumTransferEntries) return false; | 170 if (cache_size_ == max_cache_size_) return false; |
146 // Ok, we'll try to grab an entry from some other size class. | 171 // Ok, we'll try to grab an entry from some other size class. |
147 if (EvictRandomSizeClass(size_class_, false) || | 172 if (EvictRandomSizeClass(size_class_, false) || |
148 EvictRandomSizeClass(size_class_, true)) { | 173 EvictRandomSizeClass(size_class_, true)) { |
149 // Succeeded in evicting, we're going to make our cache larger. | 174 // Succeeded in evicting, we're going to make our cache larger. |
150 // However, we may have dropped and re-acquired the lock in | 175 // However, we may have dropped and re-acquired the lock in |
151 // EvictRandomSizeClass (via ShrinkCache and the LockInverter), so the | 176 // EvictRandomSizeClass (via ShrinkCache and the LockInverter), so the |
152 // cache_size may have changed. Therefore, check and verify that it is | 177 // cache_size may have changed. Therefore, check and verify that it is |
153 // still OK to increase the cache_size. | 178 // still OK to increase the cache_size. |
154 if (cache_size_ < kNumTransferEntries) { | 179 if (cache_size_ < max_cache_size_) { |
155 cache_size_++; | 180 cache_size_++; |
156 return true; | 181 return true; |
157 } | 182 } |
158 } | 183 } |
159 return false; | 184 return false; |
160 } | 185 } |
161 | 186 |
162 | 187 |
163 namespace { | 188 namespace { |
164 class LockInverter { | 189 class LockInverter { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 cache_size_--; | 226 cache_size_--; |
202 return true; | 227 return true; |
203 } | 228 } |
204 | 229 |
205 void CentralFreeList::InsertRange(void *start, void *end, int N) { | 230 void CentralFreeList::InsertRange(void *start, void *end, int N) { |
206 SpinLockHolder h(&lock_); | 231 SpinLockHolder h(&lock_); |
207 if (N == Static::sizemap()->num_objects_to_move(size_class_) && | 232 if (N == Static::sizemap()->num_objects_to_move(size_class_) && |
208 MakeCacheSpace()) { | 233 MakeCacheSpace()) { |
209 int slot = used_slots_++; | 234 int slot = used_slots_++; |
210 ASSERT(slot >=0); | 235 ASSERT(slot >=0); |
211 ASSERT(slot < kNumTransferEntries); | 236 ASSERT(slot < max_cache_size_); |
212 TCEntry *entry = &tc_slots_[slot]; | 237 TCEntry *entry = &tc_slots_[slot]; |
213 entry->head = start; | 238 entry->head = start; |
214 entry->tail = end; | 239 entry->tail = end; |
215 return; | 240 return; |
216 } | 241 } |
217 ReleaseListToSpans(start); | 242 ReleaseListToSpans(start); |
218 } | 243 } |
219 | 244 |
220 int CentralFreeList::RemoveRange(void **start, void **end, int N) { | 245 int CentralFreeList::RemoveRange(void **start, void **end, int N) { |
221 ASSERT(N > 0); | 246 ASSERT(N > 0); |
222 lock_.Lock(); | 247 lock_.Lock(); |
223 if (N == Static::sizemap()->num_objects_to_move(size_class_) && | 248 if (N == Static::sizemap()->num_objects_to_move(size_class_) && |
224 used_slots_ > 0) { | 249 used_slots_ > 0) { |
225 int slot = --used_slots_; | 250 int slot = --used_slots_; |
226 ASSERT(slot >= 0); | 251 ASSERT(slot >= 0); |
227 TCEntry *entry = &tc_slots_[slot]; | 252 TCEntry *entry = &tc_slots_[slot]; |
228 *start = entry->head; | 253 *start = entry->head; |
229 *end = entry->tail; | 254 *end = entry->tail; |
230 lock_.Unlock(); | 255 lock_.Unlock(); |
231 return N; | 256 return N; |
232 } | 257 } |
233 | 258 |
234 int result = 0; | 259 int result = 0; |
235 void* head = NULL; | 260 void* head = NULL; |
236 void* tail = NULL; | 261 void* tail = NULL; |
237 // TODO: Prefetch multiple TCEntries? | 262 // TODO: Prefetch multiple TCEntries? |
238 tail = FetchFromSpansSafe(); | 263 tail = FetchFromSpansSafe(); |
239 if (tail != NULL) { | 264 if (tail != NULL) { |
240 FL_Push(&head, tail); | 265 SLL_SetNext(tail, NULL); |
| 266 head = tail; |
241 result = 1; | 267 result = 1; |
242 while (result < N) { | 268 while (result < N) { |
243 void *t = FetchFromSpans(); | 269 void *t = FetchFromSpans(); |
244 if (!t) break; | 270 if (!t) break; |
245 FL_Push(&head, t); | 271 SLL_Push(&head, t); |
246 result++; | 272 result++; |
247 } | 273 } |
248 } | 274 } |
249 lock_.Unlock(); | 275 lock_.Unlock(); |
250 *start = head; | 276 *start = head; |
251 *end = tail; | 277 *end = tail; |
252 return result; | 278 return result; |
253 } | 279 } |
254 | 280 |
255 | 281 |
256 void* CentralFreeList::FetchFromSpansSafe() { | 282 void* CentralFreeList::FetchFromSpansSafe() { |
257 void *t = FetchFromSpans(); | 283 void *t = FetchFromSpans(); |
258 if (!t) { | 284 if (!t) { |
259 Populate(); | 285 Populate(); |
260 t = FetchFromSpans(); | 286 t = FetchFromSpans(); |
261 } | 287 } |
262 return t; | 288 return t; |
263 } | 289 } |
264 | 290 |
265 void* CentralFreeList::FetchFromSpans() { | 291 void* CentralFreeList::FetchFromSpans() { |
266 if (tcmalloc::DLL_IsEmpty(&nonempty_)) return NULL; | 292 if (tcmalloc::DLL_IsEmpty(&nonempty_)) return NULL; |
267 Span* span = nonempty_.next; | 293 Span* span = nonempty_.next; |
268 | 294 |
269 ASSERT(span->objects != NULL); | 295 ASSERT(span->objects != NULL); |
270 span->refcount++; | 296 span->refcount++; |
271 void *result = FL_Pop(&(span->objects)); | 297 void* result = span->objects; |
| 298 span->objects = *(reinterpret_cast<void**>(result)); |
272 if (span->objects == NULL) { | 299 if (span->objects == NULL) { |
273 // Move to empty list | 300 // Move to empty list |
274 tcmalloc::DLL_Remove(span); | 301 tcmalloc::DLL_Remove(span); |
275 tcmalloc::DLL_Prepend(&empty_, span); | 302 tcmalloc::DLL_Prepend(&empty_, span); |
276 Event(span, 'E', 0); | 303 Event(span, 'E', 0); |
277 } | 304 } |
278 counter_--; | 305 counter_--; |
279 return result; | 306 return result; |
280 } | 307 } |
281 | 308 |
282 // Fetch memory from the system and add to the central cache freelist. | 309 // Fetch memory from the system and add to the central cache freelist. |
283 void CentralFreeList::Populate() { | 310 void CentralFreeList::Populate() { |
284 // Release central list lock while operating on pageheap | 311 // Release central list lock while operating on pageheap |
285 lock_.Unlock(); | 312 lock_.Unlock(); |
286 const size_t npages = Static::sizemap()->class_to_pages(size_class_); | 313 const size_t npages = Static::sizemap()->class_to_pages(size_class_); |
287 | 314 |
288 Span* span; | 315 Span* span; |
289 { | 316 { |
290 SpinLockHolder h(Static::pageheap_lock()); | 317 SpinLockHolder h(Static::pageheap_lock()); |
291 span = Static::pageheap()->New(npages); | 318 span = Static::pageheap()->New(npages); |
292 if (span) Static::pageheap()->RegisterSizeClass(span, size_class_); | 319 if (span) Static::pageheap()->RegisterSizeClass(span, size_class_); |
293 } | 320 } |
294 if (span == NULL) { | 321 if (span == NULL) { |
295 MESSAGE("tcmalloc: allocation failed", npages << kPageShift); | 322 Log(kLog, __FILE__, __LINE__, |
| 323 "tcmalloc: allocation failed", npages << kPageShift); |
296 lock_.Lock(); | 324 lock_.Lock(); |
297 return; | 325 return; |
298 } | 326 } |
299 ASSERT(span->length == npages); | 327 ASSERT(span->length == npages); |
300 // Cache sizeclass info eagerly. Locking is not necessary. | 328 // Cache sizeclass info eagerly. Locking is not necessary. |
301 // (Instead of being eager, we could just replace any stale info | 329 // (Instead of being eager, we could just replace any stale info |
302 // about this span, but that seems to be no better in practice.) | 330 // about this span, but that seems to be no better in practice.) |
303 for (int i = 0; i < npages; i++) { | 331 for (int i = 0; i < npages; i++) { |
304 Static::pageheap()->CacheSizeClass(span->start + i, size_class_); | 332 Static::pageheap()->CacheSizeClass(span->start + i, size_class_); |
305 } | 333 } |
306 | 334 |
307 // Split the block into pieces and add to the free-list | 335 // Split the block into pieces and add to the free-list |
308 // TODO: coloring of objects to avoid cache conflicts? | 336 // TODO: coloring of objects to avoid cache conflicts? |
309 void* list = NULL; | 337 void** tail = &span->objects; |
310 char* ptr = reinterpret_cast<char*>(span->start << kPageShift); | 338 char* ptr = reinterpret_cast<char*>(span->start << kPageShift); |
311 char* limit = ptr + (npages << kPageShift); | 339 char* limit = ptr + (npages << kPageShift); |
312 const size_t size = Static::sizemap()->ByteSizeForClass(size_class_); | 340 const size_t size = Static::sizemap()->ByteSizeForClass(size_class_); |
313 int num = 0; | 341 int num = 0; |
314 while (ptr + size <= limit) { | 342 while (ptr + size <= limit) { |
315 FL_Push(&list, ptr); | 343 *tail = ptr; |
| 344 tail = reinterpret_cast<void**>(ptr); |
316 ptr += size; | 345 ptr += size; |
317 num++; | 346 num++; |
318 } | 347 } |
319 ASSERT(ptr <= limit); | 348 ASSERT(ptr <= limit); |
320 span->objects = list; | 349 *tail = NULL; |
321 span->refcount = 0; // No sub-object in use yet | 350 span->refcount = 0; // No sub-object in use yet |
322 | 351 |
323 // Add span to list of non-empty spans | 352 // Add span to list of non-empty spans |
324 lock_.Lock(); | 353 lock_.Lock(); |
325 tcmalloc::DLL_Prepend(&nonempty_, span); | 354 tcmalloc::DLL_Prepend(&nonempty_, span); |
| 355 ++num_spans_; |
326 counter_ += num; | 356 counter_ += num; |
327 } | 357 } |
328 | 358 |
329 int CentralFreeList::tc_length() { | 359 int CentralFreeList::tc_length() { |
330 SpinLockHolder h(&lock_); | 360 SpinLockHolder h(&lock_); |
331 return used_slots_ * Static::sizemap()->num_objects_to_move(size_class_); | 361 return used_slots_ * Static::sizemap()->num_objects_to_move(size_class_); |
332 } | 362 } |
333 | 363 |
| 364 size_t CentralFreeList::OverheadBytes() { |
| 365 SpinLockHolder h(&lock_); |
| 366 if (size_class_ == 0) { // 0 holds the 0-sized allocations |
| 367 return 0; |
| 368 } |
| 369 const size_t pages_per_span = Static::sizemap()->class_to_pages(size_class_); |
| 370 const size_t object_size = Static::sizemap()->class_to_size(size_class_); |
| 371 ASSERT(object_size > 0); |
| 372 const size_t overhead_per_span = (pages_per_span * kPageSize) % object_size; |
| 373 return num_spans_ * overhead_per_span; |
| 374 } |
| 375 |
334 } // namespace tcmalloc | 376 } // namespace tcmalloc |
OLD | NEW |