1 ///-*-C++-*-////////////////////////////////////////////////////////////////// 2 // 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 6 // 7 // Copyright (c) 1998-2000, The University of Texas at Austin. 8 // 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. 12 // 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. 17 // 18 ////////////////////////////////////////////////////////////////////////////// 19 20 #include "config.h" 21 22 #include "heap.h" 23 #include "processheap.h" 24 #include "superblock.h" 25 26 using namespace BPrivate; 27 28 // NB: Use maketable.cpp to update this 29 // if SIZE_CLASSES, ALIGNMENT, SIZE_CLASS_BASE, MAX_EMPTY_SUPERBLOCKS, 30 // or SUPERBLOCK_SIZE changes. 31 32 #if (MAX_INTERNAL_FRAGMENTATION == 2) 33 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 50 }; 51 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 61 }; 62 63 #elif (MAX_INTERNAL_FRAGMENTATION == 6) 64 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 72 }; 73 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 79 }; 80 81 #elif (MAX_INTERNAL_FRAGMENTATION == 10) 82 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, 88 2147483648UL 89 }; 90 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, 94 4UL, 4UL, 4UL, 4UL 95 }; 96 97 #else 98 # error "Undefined size class base." 99 #endif 100 101 102 int hoardHeap::fMaxThreadHeaps = 1; 103 int hoardHeap::_numProcessors; 104 int hoardHeap::_numProcessorsMask; 105 106 107 // Return ceil(log_2(num)). 108 // num must be positive. 109 110 static int 111 lg(int num) 112 { 113 assert(num > 0); 114 int power = 0; 115 int n = 1; 116 // Invariant: 2^power == n. 117 while (n < num) { 118 n <<= 1; 119 power++; 120 } 121 return power; 122 } 123 124 125 // #pragma mark - 126 127 128 hoardHeap::hoardHeap(void) 129 : 130 _index(0), _reusableSuperblocks(NULL), _reusableSuperblocksCount(0) 131 #if HEAP_DEBUG 132 , _magic(HEAP_MAGIC) 133 #endif 134 { 135 initLock(); 136 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; 141 } 142 } 143 144 for (int k = 0; k < SIZE_CLASSES; k++) { 145 _leastEmptyBin[k] = 0; 146 } 147 } 148 149 150 void 151 hoardHeap::insertSuperblock(int sizeclass, 152 superblock *sb, processHeap *pHeap) 153 { 154 assert(sb->isValid()); 155 assert(sb->getBlockSizeClass() == sizeclass); 156 assert(sb->getPrev() == NULL); 157 assert(sb->getNext() == NULL); 158 assert(_magic == HEAP_MAGIC); 159 160 // Now it's ours. 161 sb->setOwner(this); 162 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(); 167 168 // Update the stats. 169 incStats(sizeclass, sb->getNumBlocks() - sb->getNumAvailable(), 170 sb->getNumBlocks()); 171 172 if (fullness == 0 173 && sb->getNumBlocks() > 1 174 && sb->getNumBlocks() == sb->getNumAvailable()) { 175 // Recycle this superblock. 176 #if 0 177 removeSuperblock(sb, sizeclass); 178 // Update the stats. 179 decStats(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); 184 #if HEAP_LOG 185 // Record the memory deallocation. 186 MemoryRequest m; 187 m.deallocate((int)sb->getNumBlocks() * 188 (int)sizeFromClass(sb->getBlockSizeClass())); 189 pHeap->getLog(getIndex()).append(m); 190 #endif 191 #if HEAP_FRAG_STATS 192 193 pHeap->setDeallocated(0, sb->getNumBlocks() * sizeFromClass(sb->getBlockSizeClass())); 194 #endif 195 196 hoardUnsbrk(sb, align(sizeof(superblock) + blksize)); 197 #else 198 recycle(sb); 199 #endif 200 } else { 201 // Insert it into the appropriate list. 202 superblock *&head = _superblocks[fullness][sizeclass]; 203 sb->insertBefore(head); 204 head = sb; 205 assert(head->isValid()); 206 207 // Reset the least-empty bin counter. 208 _leastEmptyBin[sizeclass] = RESET_LEAST_EMPTY_BIN; 209 } 210 } 211 212 213 superblock * 214 hoardHeap::removeMaxSuperblock(int sizeclass) 215 { 216 assert(_magic == HEAP_MAGIC); 217 218 superblock *head = NULL; 219 220 // First check the reusable superblocks list. 221 222 head = reuse(sizeclass); 223 if (head) { 224 // We found one. Since we're removing this superblock, update the 225 // stats accordingly. 226 decStats(sizeclass, 227 head->getNumBlocks() - head->getNumAvailable(), 228 head->getNumBlocks()); 229 230 return head; 231 } 232 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. 239 240 int i = 0; 241 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]; 246 if (head) 247 break; 248 249 i++; 250 } 251 252 if (!head) 253 return NULL; 254 255 // Make sure that this superblock is at least 1/EMPTY_FRACTION 256 // empty. 257 assert(head->getNumAvailable() * EMPTY_FRACTION >= head->getNumBlocks()); 258 259 removeSuperblock(head, sizeclass); 260 261 assert(head->isValid()); 262 assert(head->getPrev() == NULL); 263 assert(head->getNext() == NULL); 264 return head; 265 } 266 267 268 void 269 hoardHeap::removeSuperblock(superblock *sb, int sizeclass) 270 { 271 assert(_magic == HEAP_MAGIC); 272 273 assert(sb->isValid()); 274 assert(sb->getOwner() == this); 275 assert(sb->getBlockSizeClass() == sizeclass); 276 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()); 282 } 283 break; 284 } 285 } 286 287 sb->remove(); 288 decStats(sizeclass, sb->getNumBlocks() - sb->getNumAvailable(), 289 sb->getNumBlocks()); 290 } 291 292 293 void 294 hoardHeap::moveSuperblock(superblock *sb, 295 int sizeclass, int fromBin, int toBin) 296 { 297 assert(_magic == HEAP_MAGIC); 298 assert(sb->isValid()); 299 assert(sb->getOwner() == this); 300 assert(sb->getBlockSizeClass() == sizeclass); 301 assert(sb->getFullness() == toBin); 302 303 // Remove the superblock from the old bin. 304 305 superblock *&oldHead = _superblocks[fromBin][sizeclass]; 306 if (sb == oldHead) { 307 oldHead = sb->getNext(); 308 if (oldHead != NULL) { 309 assert(oldHead->isValid()); 310 } 311 } 312 313 sb->remove(); 314 315 // Insert the superblock into the new bin. 316 317 superblock *&newHead = _superblocks[toBin][sizeclass]; 318 sb->insertBefore(newHead); 319 newHead = sb; 320 assert(newHead->isValid()); 321 322 // Reset the least-empty bin counter. 323 _leastEmptyBin[sizeclass] = RESET_LEAST_EMPTY_BIN; 324 } 325 326 327 // The heap lock must be held when this procedure is called. 328 329 int 330 hoardHeap::freeBlock(block * &b, superblock * &sb, 331 int sizeclass, processHeap *pHeap) 332 { 333 assert(sb->isValid()); 334 assert(b->isValid()); 335 assert(this == sb->getOwner()); 336 337 const int oldFullness = sb->getFullness(); 338 sb->putBlock(b); 339 decUStats(sizeclass); 340 const int newFullness = sb->getFullness(); 341 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); 347 #if HEAP_LOG 348 // Record the memory deallocation. 349 MemoryRequest m; 350 m.deallocate((int)sb->getNumBlocks() 351 * (int)sizeFromClass(sb->getBlockSizeClass())); 352 pHeap->getLog(getIndex()).append(m); 353 #endif 354 #if HEAP_FRAG_STATS 355 pHeap->setDeallocated(0, 356 sb->getNumBlocks() * sizeFromClass(sb->getBlockSizeClass())); 357 #endif 358 hoardUnsbrk(sb, align(sizeof(superblock) + blksize)); 359 return 1; 360 } 361 362 // If the fullness value has changed, move the superblock. 363 if (newFullness != oldFullness) { 364 moveSuperblock(sb, sizeclass, oldFullness, newFullness); 365 } else { 366 // Move the superblock to the front of its list (to reduce 367 // paging). 368 superblock *&head = _superblocks[newFullness][sizeclass]; 369 if (sb != head) { 370 sb->remove(); 371 sb->insertBefore(head); 372 head = sb; 373 } 374 } 375 376 // If the superblock is now empty, recycle it. 377 378 if ((newFullness == 0) && (sb->getNumBlocks() == sb->getNumAvailable())) { 379 removeSuperblock(sb, sizeclass); 380 #if 0 381 // Free it immediately. 382 const size_t s = sizeFromClass(sizeclass); 383 const int blksize = align(sizeof(block) + s); 384 #if HEAP_LOG 385 // Record the memory deallocation. 386 MemoryRequest m; 387 m.deallocate((int)sb->getNumBlocks() 388 * (int)sizeFromClass(sb->getBlockSizeClass())); 389 pHeap->getLog(getIndex()).append(m); 390 #endif 391 #if HEAP_FRAG_STATS 392 pHeap->setDeallocated(0, 393 sb->getNumBlocks() * sizeFromClass(sb->getBlockSizeClass())); 394 #endif 395 396 hoardUnsbrk(sb, align(sizeof(superblock) + blksize)); 397 return 1; 398 #else 399 recycle(sb); 400 // Update the stats. This restores the stats to their state 401 // before the call to removeSuperblock, above. 402 incStats(sizeclass, 403 sb->getNumBlocks() - sb->getNumAvailable(), sb->getNumBlocks()); 404 #endif 405 } 406 407 // If this is the process heap, then we're done. 408 if (this == (hoardHeap *)pHeap) 409 return 0; 410 411 // 412 // Release a superblock, if necessary. 413 // 414 415 // 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. 419 // 420 421 // We never move anything to the process heap if we're on a 422 // uniprocessor. 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)) { 429 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); 434 435 // Update the statistics. 436 437 assert(maxSb->getNumBlocks() >= maxSb->getNumAvailable()); 438 439 // Give the superblock back to the process heap. 440 pHeap->release(maxSb); 441 } 442 } 443 444 return 0; 445 } 446 447 448 void 449 hoardHeap::initNumProcs(void) 450 { 451 system_info info; 452 if (get_system_info(&info) != B_OK) 453 hoardHeap::_numProcessors = 1; 454 else 455 hoardHeap::_numProcessors = info.cpu_count; 456 457 fMaxThreadHeaps = 1 << (lg(_numProcessors) + 1); 458 _numProcessorsMask = fMaxThreadHeaps - 1; 459 } 460 461