1 ///-*-C++-*-//////////////////////////////////////////////////////////////////
3 // Hoard: A Fast, Scalable, and Memory-Efficient Allocator
4 // for Shared-Memory Multiprocessors
5 // Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
7 // Copyright (c) 1998-2000, The University of Texas at Austin.
9 // This library is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU Library General Public License as
11 // published by the Free Software Foundation, http://www.fsf.org.
13 // This library is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 //////////////////////////////////////////////////////////////////////////////
23 #include "processheap.h"
24 #include "superblock.h"
26 using namespace BPrivate
;
28 // NB: Use maketable.cpp to update this
29 // if SIZE_CLASSES, ALIGNMENT, SIZE_CLASS_BASE, MAX_EMPTY_SUPERBLOCKS,
30 // or SUPERBLOCK_SIZE changes.
32 #if (MAX_INTERNAL_FRAGMENTATION == 2)
34 size_t hoardHeap::_sizeTable
[hoardHeap::SIZE_CLASSES
] = {
35 8UL, 16UL, 24UL, 32UL, 40UL, 48UL, 56UL, 72UL, 80UL, 96UL, 120UL, 144UL,
36 168UL, 200UL, 240UL, 288UL, 344UL, 416UL, 496UL, 592UL, 712UL, 856UL,
37 1024UL, 1232UL, 1472UL, 1768UL, 2120UL, 2544UL, 3048UL, 3664UL,
38 4392UL, 5272UL, 6320UL, 7584UL, 9104UL, 10928UL, 13112UL, 15728UL,
39 18872UL, 22648UL, 27176UL, 32616UL, 39136UL, 46960UL, 56352UL,
40 67624UL, 81144UL, 97376UL, 116848UL, 140216UL, 168256UL, 201904UL,
41 242288UL, 290744UL, 348896UL, 418672UL, 502408UL, 602888UL, 723464UL,
42 868152UL, 1041784UL, 1250136UL, 1500160UL, 1800192UL, 2160232UL,
43 2592280UL, 3110736UL, 3732880UL, 4479456UL, 5375344UL, 6450408UL,
44 7740496UL, 9288592UL, 11146312UL, 13375568UL, 16050680UL, 19260816UL,
45 23112984UL, 27735576UL, 33282688UL, 39939224UL, 47927072UL,
46 57512488UL, 69014984UL, 82817976UL, 99381576UL, 119257888UL,
47 143109472UL, 171731360UL, 206077632UL, 247293152UL, 296751776UL,
48 356102144UL, 427322560UL, 512787072UL, 615344512UL, 738413376UL,
49 886096064UL, 1063315264UL
52 size_t hoardHeap::_threshold
[hoardHeap::SIZE_CLASSES
] = {
53 4096UL, 2048UL, 1364UL, 1024UL, 816UL, 680UL, 584UL, 452UL, 408UL,
54 340UL, 272UL, 224UL, 192UL, 160UL, 136UL, 112UL, 92UL, 76UL, 64UL,
55 52UL, 44UL, 36UL, 32UL, 24UL, 20UL, 16UL, 12UL, 12UL, 8UL, 8UL, 4UL,
56 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
57 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
58 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
59 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
60 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL
63 #elif (MAX_INTERNAL_FRAGMENTATION == 6)
65 size_t hoardHeap::_sizeTable
[hoardHeap::SIZE_CLASSES
] = {
66 8UL, 16UL, 24UL, 32UL, 48UL, 72UL, 112UL, 176UL, 288UL, 456UL, 728UL,
67 1160UL, 1848UL, 2952UL, 4728UL, 7560UL, 12096UL, 19344UL, 30952UL,
68 49520UL, 79232UL, 126768UL, 202832UL, 324520UL, 519232UL, 830768UL,
69 1329232UL, 2126768UL, 3402824UL, 5444520UL, 8711232UL, 13937968UL,
70 22300752UL, 35681200UL, 57089912UL, 91343856UL, 146150176UL,
71 233840256UL, 374144416UL, 598631040UL, 957809728UL, 1532495488UL
74 size_t hoardHeap::_threshold
[hoardHeap::SIZE_CLASSES
] = {
75 4096UL, 2048UL, 1364UL, 1024UL, 680UL, 452UL, 292UL, 184UL, 112UL, 68UL,
76 44UL, 28UL, 16UL, 8UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
77 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
78 4UL, 4UL, 4UL, 4UL, 4UL
81 #elif (MAX_INTERNAL_FRAGMENTATION == 10)
83 size_t hoardHeap::_sizeTable
[hoardHeap::SIZE_CLASSES
] = {
84 8UL, 16UL, 32UL, 64UL, 128UL, 256UL, 512UL, 1024UL, 2048UL, 4096UL,
85 8192UL, 16384UL, 32768UL, 65536UL, 131072UL, 262144UL, 524288UL,
86 1048576UL, 2097152UL, 4194304UL, 8388608UL, 16777216UL, 33554432UL,
87 67108864UL, 134217728UL, 268435456UL, 536870912UL, 1073741824UL,
91 size_t hoardHeap::_threshold
[hoardHeap::SIZE_CLASSES
] = {
92 4096UL, 2048UL, 1024UL, 512UL, 256UL, 128UL, 64UL, 32UL, 16UL, 8UL, 4UL,
93 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
98 # error "Undefined size class base."
102 int hoardHeap::fMaxThreadHeaps
= 1;
103 int hoardHeap::_numProcessors
;
104 int hoardHeap::_numProcessorsMask
;
107 // Return ceil(log_2(num)).
108 // num must be positive.
116 // Invariant: 2^power == n.
128 hoardHeap::hoardHeap(void)
130 _index(0), _reusableSuperblocks(NULL
), _reusableSuperblocksCount(0)
137 for (int i
= 0; i
< SUPERBLOCK_FULLNESS_GROUP
; i
++) {
138 for (int j
= 0; j
< SIZE_CLASSES
; j
++) {
139 // Initialize all superblocks lists to empty.
140 _superblocks
[i
][j
] = NULL
;
144 for (int k
= 0; k
< SIZE_CLASSES
; k
++) {
145 _leastEmptyBin
[k
] = 0;
151 hoardHeap::insertSuperblock(int sizeclass
,
152 superblock
*sb
, processHeap
*pHeap
)
154 assert(sb
->isValid());
155 assert(sb
->getBlockSizeClass() == sizeclass
);
156 assert(sb
->getPrev() == NULL
);
157 assert(sb
->getNext() == NULL
);
158 assert(_magic
== HEAP_MAGIC
);
163 // How full is this superblock? We'll use this information to put
164 // it into the right 'bin'.
165 sb
->computeFullness();
166 int fullness
= sb
->getFullness();
169 incStats(sizeclass
, sb
->getNumBlocks() - sb
->getNumAvailable(),
173 && sb
->getNumBlocks() > 1
174 && sb
->getNumBlocks() == sb
->getNumAvailable()) {
175 // Recycle this superblock.
177 removeSuperblock(sb
, sizeclass
);
180 sb
->getNumBlocks() - sb
->getNumAvailable(), sb
->getNumBlocks());
181 // Free it immediately.
182 const size_t s
= sizeFromClass(sizeclass
);
183 const int blksize
= align(sizeof(block
) + s
);
185 // Record the memory deallocation.
187 m
.deallocate((int)sb
->getNumBlocks() *
188 (int)sizeFromClass(sb
->getBlockSizeClass()));
189 pHeap
->getLog(getIndex()).append(m
);
193 pHeap
->setDeallocated(0, sb
->getNumBlocks() * sizeFromClass(sb
->getBlockSizeClass()));
196 hoardUnsbrk(sb
, align(sizeof(superblock
) + blksize
));
201 // Insert it into the appropriate list.
202 superblock
*&head
= _superblocks
[fullness
][sizeclass
];
203 sb
->insertBefore(head
);
205 assert(head
->isValid());
207 // Reset the least-empty bin counter.
208 _leastEmptyBin
[sizeclass
] = RESET_LEAST_EMPTY_BIN
;
214 hoardHeap::removeMaxSuperblock(int sizeclass
)
216 assert(_magic
== HEAP_MAGIC
);
218 superblock
*head
= NULL
;
220 // First check the reusable superblocks list.
222 head
= reuse(sizeclass
);
224 // We found one. Since we're removing this superblock, update the
225 // stats accordingly.
227 head
->getNumBlocks() - head
->getNumAvailable(),
228 head
->getNumBlocks());
233 // Instead of finding the superblock with the most available space
234 // (something that would either involve a linear scan through the
235 // superblocks or maintaining the superblocks in sorted order), we
236 // just pick one that is no more than
237 // 1/(SUPERBLOCK_FULLNESS_GROUP-1) more full than the superblock
238 // with the most available space. We start with the emptiest group.
242 // Note: the last group (SUPERBLOCK_FULLNESS_GROUP - 1) is full, so
243 // we never need to check it. But for robustness, we leave it in.
244 while (i
< SUPERBLOCK_FULLNESS_GROUP
) {
245 head
= _superblocks
[i
][sizeclass
];
255 // Make sure that this superblock is at least 1/EMPTY_FRACTION
257 assert(head
->getNumAvailable() * EMPTY_FRACTION
>= head
->getNumBlocks());
259 removeSuperblock(head
, sizeclass
);
261 assert(head
->isValid());
262 assert(head
->getPrev() == NULL
);
263 assert(head
->getNext() == NULL
);
269 hoardHeap::removeSuperblock(superblock
*sb
, int sizeclass
)
271 assert(_magic
== HEAP_MAGIC
);
273 assert(sb
->isValid());
274 assert(sb
->getOwner() == this);
275 assert(sb
->getBlockSizeClass() == sizeclass
);
277 for (int i
= 0; i
< SUPERBLOCK_FULLNESS_GROUP
; i
++) {
278 if (sb
== _superblocks
[i
][sizeclass
]) {
279 _superblocks
[i
][sizeclass
] = sb
->getNext();
280 if (_superblocks
[i
][sizeclass
] != NULL
) {
281 assert(_superblocks
[i
][sizeclass
]->isValid());
288 decStats(sizeclass
, sb
->getNumBlocks() - sb
->getNumAvailable(),
294 hoardHeap::moveSuperblock(superblock
*sb
,
295 int sizeclass
, int fromBin
, int toBin
)
297 assert(_magic
== HEAP_MAGIC
);
298 assert(sb
->isValid());
299 assert(sb
->getOwner() == this);
300 assert(sb
->getBlockSizeClass() == sizeclass
);
301 assert(sb
->getFullness() == toBin
);
303 // Remove the superblock from the old bin.
305 superblock
*&oldHead
= _superblocks
[fromBin
][sizeclass
];
307 oldHead
= sb
->getNext();
308 if (oldHead
!= NULL
) {
309 assert(oldHead
->isValid());
315 // Insert the superblock into the new bin.
317 superblock
*&newHead
= _superblocks
[toBin
][sizeclass
];
318 sb
->insertBefore(newHead
);
320 assert(newHead
->isValid());
322 // Reset the least-empty bin counter.
323 _leastEmptyBin
[sizeclass
] = RESET_LEAST_EMPTY_BIN
;
327 // The heap lock must be held when this procedure is called.
330 hoardHeap::freeBlock(block
* &b
, superblock
* &sb
,
331 int sizeclass
, processHeap
*pHeap
)
333 assert(sb
->isValid());
334 assert(b
->isValid());
335 assert(this == sb
->getOwner());
337 const int oldFullness
= sb
->getFullness();
339 decUStats(sizeclass
);
340 const int newFullness
= sb
->getFullness();
342 // Free big superblocks.
343 if (sb
->getNumBlocks() == 1) {
344 removeSuperblock(sb
, sizeclass
);
345 const size_t s
= sizeFromClass(sizeclass
);
346 const int blksize
= align(sizeof(block
) + s
);
348 // Record the memory deallocation.
350 m
.deallocate((int)sb
->getNumBlocks()
351 * (int)sizeFromClass(sb
->getBlockSizeClass()));
352 pHeap
->getLog(getIndex()).append(m
);
355 pHeap
->setDeallocated(0,
356 sb
->getNumBlocks() * sizeFromClass(sb
->getBlockSizeClass()));
358 hoardUnsbrk(sb
, align(sizeof(superblock
) + blksize
));
362 // If the fullness value has changed, move the superblock.
363 if (newFullness
!= oldFullness
) {
364 moveSuperblock(sb
, sizeclass
, oldFullness
, newFullness
);
366 // Move the superblock to the front of its list (to reduce
368 superblock
*&head
= _superblocks
[newFullness
][sizeclass
];
371 sb
->insertBefore(head
);
376 // If the superblock is now empty, recycle it.
378 if ((newFullness
== 0) && (sb
->getNumBlocks() == sb
->getNumAvailable())) {
379 removeSuperblock(sb
, sizeclass
);
381 // Free it immediately.
382 const size_t s
= sizeFromClass(sizeclass
);
383 const int blksize
= align(sizeof(block
) + s
);
385 // Record the memory deallocation.
387 m
.deallocate((int)sb
->getNumBlocks()
388 * (int)sizeFromClass(sb
->getBlockSizeClass()));
389 pHeap
->getLog(getIndex()).append(m
);
392 pHeap
->setDeallocated(0,
393 sb
->getNumBlocks() * sizeFromClass(sb
->getBlockSizeClass()));
396 hoardUnsbrk(sb
, align(sizeof(superblock
) + blksize
));
400 // Update the stats. This restores the stats to their state
401 // before the call to removeSuperblock, above.
403 sb
->getNumBlocks() - sb
->getNumAvailable(), sb
->getNumBlocks());
407 // If this is the process heap, then we're done.
408 if (this == (hoardHeap
*)pHeap
)
412 // Release a superblock, if necessary.
416 // Check to see if the amount free exceeds the release threshold
417 // (two superblocks worth of blocks for a given sizeclass) and if
418 // the heap is sufficiently empty.
421 // We never move anything to the process heap if we're on a
423 if (_numProcessors
> 1) {
424 int inUse
, allocated
;
425 getStats(sizeclass
, inUse
, allocated
);
426 if ((inUse
< allocated
- getReleaseThreshold(sizeclass
))
427 && (EMPTY_FRACTION
* inUse
<
428 EMPTY_FRACTION
* allocated
- allocated
)) {
430 // We've crossed the magical threshold. Find the superblock with
431 // the most free blocks and give it to the process heap.
432 superblock
*const maxSb
= removeMaxSuperblock(sizeclass
);
433 assert(maxSb
!= NULL
);
435 // Update the statistics.
437 assert(maxSb
->getNumBlocks() >= maxSb
->getNumAvailable());
439 // Give the superblock back to the process heap.
440 pHeap
->release(maxSb
);
449 hoardHeap::initNumProcs(void)
452 if (get_system_info(&info
) != B_OK
)
453 hoardHeap::_numProcessors
= 1;
455 hoardHeap::_numProcessors
= info
.cpu_count
;
457 fMaxThreadHeaps
= 1 << (lg(_numProcessors
) + 1);
458 _numProcessorsMask
= fMaxThreadHeaps
- 1;