usermode/library/pmalloc/src/hoardheap.cpp

00001 
00002 //
00003 // The Hoard Multiprocessor Memory Allocator
00004 // www.hoard.org
00005 //
00006 // Author: Emery Berger, http://www.cs.utexas.edu/users/emery
00007 //
00008 // Copyright (c) 1998-2001, The University of Texas at Austin.
00009 //
00010 // This library is free software; you can redistribute it and/or modify
00011 // it under the terms of the GNU Library General Public License as
00012 // published by the Free Software Foundation, http://www.fsf.org.
00013 //
00014 // This library is distributed in the hope that it will be useful, but
00015 // WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Library General Public License for more details.
00018 //
00020 #include "config.h"
00021 
00022 #include "hoardheap.h"
00023 #include "processheap.h"
00024 #include "persistentheap.h"
00025 #include "superblock.h"
00026 
00027 // Use the Python script gen_sizeclass.py to generate the tables
00028 // for a specific superblock size:
00029 //   _sizeTable 
00030 //   _threshold
00031 
00032 size_t hoardHeap::_sizeTable[hoardHeap::SIZE_CLASSES]
00033 = {8UL, 16UL, 24UL, 32UL, 40UL, 48UL, 56UL, 64UL, 72UL, 80UL, 88UL, 96UL, 104UL, 112UL, 120UL, 128UL, 136UL, 144UL, 152UL, 160UL, 168UL, 176UL, 184UL, 192UL, 200UL, 208UL, 216UL, 224UL, 232UL, 240UL, 248UL, 256UL, 264UL, 272UL, 280UL, 288UL, 296UL, 304UL, 312UL, 320UL, 328UL, 336UL, 344UL, 352UL, 360UL, 368UL, 376UL, 384UL, 392UL, 400UL, 408UL, 416UL, 424UL, 432UL, 440UL, 448UL, 456UL, 464UL, 472UL, 480UL, 488UL, 496UL, 504UL, 512UL, 576UL, 640UL, 704UL, 768UL, 832UL, 896UL, 960UL, 1024UL, 1088UL, 1152UL, 1216UL, 1280UL, 1344UL, 1408UL, 1472UL, 1536UL, 1600UL, 1664UL, 1728UL, 1792UL, 1856UL, 1920UL, 1984UL, 2048UL, 2112UL, 2560UL, 3072UL, 3584UL, 4096UL, 4608UL, 5120UL, 5632UL, 6144UL, 6656UL, 7168UL, 7680UL, 8192UL, 8704UL, 9216UL, 9728UL, 10240UL, 10752UL, 12288UL, 16384UL, 20480UL, 24576UL, 28672UL, 32768UL, 36864UL, 40960UL, 65536UL, 98304UL, 131072UL, 163840UL, 262144UL, 524288UL, 1048576UL, 2097152UL, 4194304UL, 8388608UL, 16777216UL, 33554432UL, 67108864UL, 134217728UL, 268435456UL, 536870912UL, 1073741824UL, 2147483648UL};
00034 
00035 size_t hoardHeap::_threshold[hoardHeap::SIZE_CLASSES]
00036 = {2048UL, 1024UL, 682UL, 512UL, 409UL, 341UL, 292UL, 256UL, 227UL, 204UL, 186UL, 170UL, 157UL, 146UL, 136UL, 128UL, 120UL, 113UL, 107UL, 102UL, 97UL, 93UL, 89UL, 85UL, 81UL, 78UL, 75UL, 73UL, 70UL, 68UL, 66UL, 64UL, 62UL, 60UL, 58UL, 56UL, 55UL, 53UL, 52UL, 51UL, 49UL, 48UL, 47UL, 46UL, 45UL, 44UL, 43UL, 42UL, 41UL, 40UL, 40UL, 39UL, 38UL, 37UL, 37UL, 36UL, 35UL, 35UL, 34UL, 34UL, 33UL, 33UL, 32UL, 32UL, 28UL, 25UL, 23UL, 21UL, 19UL, 18UL, 17UL, 16UL, 15UL, 14UL, 13UL, 12UL, 12UL, 11UL, 11UL, 10UL, 10UL, 9UL, 9UL, 9UL, 8UL, 8UL, 8UL, 8UL, 7UL, 6UL, 5UL, 4UL, 4UL, 3UL, 3UL, 2UL, 2UL, 2UL, 2UL, 2UL, 2UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL, 1UL};
00037 
00038 
00039 hoardHeap::hoardHeap (void)
00040   : _index (0),
00041     _reusableSuperblocks (NULL),
00042     _reusableSuperblocksCount (0)
00043 #if HEAP_DEBUG
00044   , _magic (HEAP_MAGIC)
00045 #endif
00046 {
00047   // Initialize the per-heap lock.
00048   hoardLockInit (_lock);
00049   for (int i = 0; i < SUPERBLOCK_FULLNESS_GROUP; i++) {
00050     for (int j = 0; j < SIZE_CLASSES; j++) {
00051       // Initialize all superblocks lists to empty.
00052       _superblocks[i][j] = NULL;
00053     }
00054   }
00055   // Hoard resets leastEmptyBin[k] to 0 because initially bins
00056   // contain no superblocks. However, in our case the persistent
00057   // heap may have partially-full superblocks. So if 
00058   // findAvailableSuperblock does not find any superblock it checks
00059   // whether the persistentHeap has any. To make this work correctly
00060   // leastEmptyBin has to be set to RESET_LEAST_EMPTY_BIN to make
00061   // sure all bins are checked at least once. 
00062   for (int k = 0; k < SIZE_CLASSES; k++) {
00063     //_leastEmptyBin[k] = 0;
00064     _leastEmptyBin[k] = RESET_LEAST_EMPTY_BIN;
00065   }
00066 }
00067 
00068 // FIXME:
00069 // Currently findAvailableSuperblock will check whether the persistent heap
00070 // has any partially-full superblock if the current heap does not. 
00071 // This lazy approach avoids distributing persistent superblocks to thread
00072 // heaps when the allocator is first initialized. However it seems that it
00073 // complicated the recycling policy. Consider revising the policy.
00074 superblock * hoardHeap::findAvailableSuperblock (int sizeclass,
00075                                                  block *& b,
00076                                                  persistentHeap * persistentheap)
00077 {
00078   assert (this);
00079   assert (_magic == HEAP_MAGIC);
00080   assert (sizeclass >= 0);
00081   assert (sizeclass < SIZE_CLASSES);
00082 
00083   superblock * sb = NULL;
00084   int reUsed = 0;
00085 
00086   // Look through the superblocks, starting with the almost-full ones
00087   // and going to the emptiest ones.  The Least Empty Bin for a
00088   // sizeclass is a conservative approximation (fixed after one
00089   // iteration) of the first bin that has superblocks in it, starting
00090   // with (surprise) the least-empty bin.
00091   for (int i = _leastEmptyBin[sizeclass]; i >= 0; i--) {
00092     sb = _superblocks[i][sizeclass];
00093         if (sb == NULL) {
00094       // Check whether the persistent heap has a superblock of fullness i and 
00095           // size class sizeclass available.
00096       sb = persistentheap->removePartiallyFullSuperblock(i, sizeclass);
00097       if (sb) {
00098         // Insert it into the bin list.
00099 
00100         insertSuperblock (sizeclass, sb, persistentheap);
00101 
00102         assert (sb->getOwner() == this);
00103         break;
00104           }
00105       if (i == _leastEmptyBin[sizeclass]) {
00106         // There wasn't a superblock in this bin,
00107         // so we adjust the least empty bin.
00108         _leastEmptyBin[sizeclass]--;
00109       }
00110     } else {
00111       assert (sb->getOwner() == this);
00112       break;
00113     }
00114   } 
00115 
00116   if (sb == NULL) {
00117     // Try to reuse a superblock.
00118     sb = reuse (sizeclass);
00119     if (sb) {
00120       assert (sb->getOwner() == this);
00121       reUsed = 1;
00122     }
00123   }
00124 
00125   if (sb != NULL) {
00126     // Sanity checks:
00127     //   This superblock is 'valid'.
00128     assert (sb->isValid());
00129     //   This superblock has the right ownership.
00130     assert (sb->getOwner() == this);
00131     
00132     int oldFullness = sb->getFullness();
00133 
00134     // Now get a block from the superblock.
00135     // This superblock must have space available.
00136     b = sb->acquireBlock();
00137     assert (b != NULL);
00138     
00139     // Update the stats.
00140     incUStats (sizeclass);
00141     
00142     if (reUsed) {
00143       insertSuperblock (sizeclass, sb, persistentheap);
00144       // Fix the stats (since insert will just have incremented them
00145       // by this amount).
00146       decStats (sizeclass,
00147                 sb->getNumBlocks() - sb->getNumAvailable(),
00148                 sb->getNumBlocks());
00149     } else {
00150       // If we've crossed a fullness group,
00151       // move the superblock.
00152       int fullness = sb->getFullness();
00153       
00154       if (fullness != oldFullness) {
00155         // Move the superblock.
00156         moveSuperblock (sb, sizeclass, oldFullness, fullness);
00157       }
00158     }
00159   }
00160 
00161   // Either we didn't find a superblock or we did and got a block.
00162   assert ((sb == NULL) || (b != NULL));
00163   // Either we didn't get a block or we did and we also got a superblock.
00164   assert ((b == NULL) || (sb != NULL));
00165 
00166   return sb;
00167 }
00168 
00169 
00170 
00171 void hoardHeap::insertSuperblock (int sizeclass,
00172                                   superblock * sb,
00173                                   persistentHeap *)
00174 {
00175         assert (sb->isValid());
00176         assert (sb->getBlockSizeClass() == sizeclass);
00177         assert (sb->getPrev() == NULL);
00178         assert (sb->getNext() == NULL);
00179         assert (_magic == HEAP_MAGIC);
00180 
00181         // Now it's ours.
00182         sb->setOwner (this);
00183 
00184         // How full is this superblock?  We'll use this information to put
00185         // it into the right 'bin'.
00186         sb->computeFullness();
00187         int fullness = sb->getFullness();
00188 
00189         // Update the stats.
00190         incStats (sizeclass,
00191                   sb->getNumBlocks() - sb->getNumAvailable(),
00192                   sb->getNumBlocks());
00193 
00194 #if 0
00195         printf("hoardHeap::insertSuperBlock sb: %p\n", sb);
00196         printf("\tfullness: %d\n", fullness);
00197         printf("\tsizeclass: %d\n", sizeclass);
00198         printf("\tnumAvailable: %d\n", sb->getNumAvailable());
00199         printf("\tnumBlocks: %d\n", sb->getNumBlocks());
00200 #endif
00201 
00202         if ((fullness == 0) &&
00203             (sb->getNumBlocks() > 1) &&
00204             (sb->getNumBlocks() == sb->getNumAvailable())) 
00205         {
00206                 // This superblock is empty --
00207                 // recycle this superblock (make it available for any sizeclass).
00208                 recycle (sb);
00209         } else {
00210                 // Insert it into the appropriate list.
00211                 superblock *& head = _superblocks[fullness][sizeclass];
00212                 sb->insertBefore (head);
00213                 head = sb;
00214                 assert (head->isValid());
00215     
00216                 // Reset the least-empty bin counter.
00217                 _leastEmptyBin[sizeclass] = RESET_LEAST_EMPTY_BIN;
00218         }
00219 }
00220 
00221 
00222 void hoardHeap::printSuperblockList()
00223 {
00224   superblock *sb = NULL;
00225 
00226   std::cout  << "printSuperblockList" << std::endl;
00227   for (int i = 0; i < 9; i--) {
00228     sb = _superblocks[i][12];
00229         if (sb) {
00230                 std::cout << "sb: " << sb << std::endl;
00231                 std::cout << "  ->psb: " << sb->getPersistentSuperblock() << std::endl;
00232         }       
00233   }
00234 }
00235 
00236 
00237 superblock * hoardHeap::removeMaxSuperblock (int sizeclass)
00238 {
00239         assert (_magic == HEAP_MAGIC);
00240 
00241         superblock * head = NULL;
00242 
00243         // First check the reusable superblocks list.
00244         head = reuse (sizeclass);
00245 
00246         if (head) {
00247                 // We found one. Since we're removing this superblock, update the
00248                 // stats accordingly.
00249                 decStats (sizeclass,
00250                           0,
00251                           head->getNumBlocks());
00252 
00253                 return head;
00254         }
00255         // Instead of finding the superblock with the most available space
00256         // (something that would either involve a linear scan through the
00257         // superblocks or maintaining the superblocks in sorted order), we
00258         // just pick one that is no more than
00259         // 1/(SUPERBLOCK_FULLNESS_GROUP-1) more full than the superblock
00260         // with the most available space.  We start with the emptiest group.
00261 
00262         int i = 0;
00263 
00264         // HARIS: Doing what is suggested by the comment below could give us
00265         // a full block. The original code does this for robustness because it
00266         // is captured in the assertion check that I commented out. So I don't
00267         // check the last group.
00268         //
00269         // Note: the last group (SUPERBLOCK_FULLNESS_GROUP - 1) is full, so
00270         // we never need to check it. But for robustness, we leave it in.
00271         // while (i < SUPERBLOCK_FULLNESS_GROUP) {
00272         while (i < (SUPERBLOCK_FULLNESS_GROUP - 1)) {
00273                 head = _superblocks[i][sizeclass];
00274                 if (head) {
00275                         break;
00276                 }
00277                 i++;
00278         }
00279 
00280         if (!head) {
00281                 return NULL;
00282         }
00283 
00284         // When we reincarnate, we bring any partially filled superblocks into 
00285         // the _superblocks array but we don't enforce Hoard's invariant: 
00286         //   (head->getNumAvailable() * EMPTY_FRACTION >= head->getNumBlocks())
00287         // because this would lead to a memory leak. 
00288 
00289         // Make sure that this superblock is at least 1/EMPTY_FRACTION empty.
00290         //assert (head->getNumAvailable() * EMPTY_FRACTION >= head->getNumBlocks());
00291 
00292         removeSuperblock (head, sizeclass);
00293 
00294         assert (head->isValid());
00295         assert (head->getPrev() == NULL);
00296         assert (head->getNext() == NULL);
00297 
00298         return head;
00299 }
00300 
00301 superblock * hoardHeap::removePartiallyFullSuperblock (int fullness, int sizeclass)
00302 {
00303         assert (_magic == HEAP_MAGIC);
00304 
00305         superblock * head = NULL;
00306 
00307         head = _superblocks[fullness][sizeclass];
00308 
00309         if (!head) {
00310                 return NULL;
00311         }
00312 
00313         // When we reincarnate, we bring any partially filled superblocks into 
00314         // the _superblocks array but we don't enforce Hoard's invariant: 
00315         //   (head->getNumAvailable() * EMPTY_FRACTION >= head->getNumBlocks())
00316         // because this would lead to a memory leak. 
00317 
00318         // Make sure that this superblock is at least 1/EMPTY_FRACTION empty.
00319         //assert (head->getNumAvailable() * EMPTY_FRACTION >= head->getNumBlocks());
00320 
00321         removeSuperblock (head, sizeclass);
00322 
00323         assert (head->isValid());
00324         assert (head->getPrev() == NULL);
00325         assert (head->getNext() == NULL);
00326         return head;
00327 }
00328 
00329 
00330 void hoardHeap::removeSuperblock (superblock * sb,
00331                                   int sizeclass)
00332 {
00333         assert (_magic == HEAP_MAGIC);
00334 
00335         assert (sb->isValid());
00336         assert (sb->getOwner() == this);
00337         assert (sb->getBlockSizeClass() == sizeclass);
00338 
00339         for (int i = 0; i < SUPERBLOCK_FULLNESS_GROUP; i++) {
00340                 if (sb == _superblocks[i][sizeclass]) {
00341                         _superblocks[i][sizeclass] = sb->getNext();
00342                         if (_superblocks[i][sizeclass] != NULL) {
00343                                 assert (_superblocks[i][sizeclass]->isValid());
00344                         }
00345                         break;
00346                 }
00347         }
00348 
00349         sb->remove();
00350         decStats (sizeclass, sb->getNumBlocks() - sb->getNumAvailable(), sb->getNumBlocks());
00351 }
00352 
00353 
00354 void hoardHeap::moveSuperblock (superblock * sb,
00355                                 int sizeclass,
00356                                 int fromBin,
00357                                 int toBin)
00358 {
00359         assert (_magic == HEAP_MAGIC);
00360         assert (sb->isValid());
00361         assert (sb->getOwner() == this);
00362         assert (sb->getBlockSizeClass() == sizeclass);
00363         assert (sb->getFullness() == toBin);
00364 
00365         // Remove the superblock from the old bin.
00366 
00367         superblock *& oldHead = _superblocks[fromBin][sizeclass];
00368         if (sb == oldHead) {
00369                 oldHead = sb->getNext();
00370                 if (oldHead != NULL) {
00371                         assert (oldHead->isValid());
00372                 }
00373         }
00374 
00375         sb->remove();
00376 
00377         // Insert the superblock into the new bin.
00378 
00379         superblock *& newHead = _superblocks[toBin][sizeclass];
00380         sb->insertBefore (newHead);
00381         newHead = sb;
00382         assert (newHead->isValid());
00383 
00384         // Reset the least-empty bin counter.
00385         _leastEmptyBin[sizeclass] = RESET_LEAST_EMPTY_BIN;
00386 }
00387 
00388 
00389 // The heap lock must be held when this procedure is called.
00390 
00391 int hoardHeap::freeBlock (block *& b,
00392                            superblock *& sb,
00393                            int sizeclass,
00394                            persistentHeap * persistentheap)
00395 {
00396         assert (sb->isValid());
00397         assert (b->isValid());
00398         assert (this == sb->getOwner());
00399 
00400         const int oldFullness = sb->getFullness();
00401         sb->putBlock (b);
00402 #if 0 // DEBUGPRINT
00403         std::cout << "hoardheap::freeBlock  hoardheap(this) = " << this 
00404                   << "sb = " << sb
00405                   << ", b = " << b
00406                   << ", sizeclass = " << sizeclass
00407                   << ", size = " << sizeFromClass(sizeclass) << std::endl;
00408 #endif
00409         decUStats (sizeclass);
00410         const int newFullness = sb->getFullness();
00411   
00412         // Free big superblocks.
00413         if (sb->getNumBlocks() == 1) {
00414                 //TODO: currently our persistent allocator handles blocks of size smaller that superblock size
00415                 assert (0);
00416             removeSuperblock (sb, sizeclass);
00417 #if HEAP_LOG
00418                 // Record the memory deallocation.
00419                 MemoryRequest m;
00420                 m.deallocate ((int) sb->getNumBlocks() * (int) sizeFromClass(sb->getBlockSizeClass()));
00421                 persistentheap->getLog(getIndex()).append(m);
00422 #endif
00423 #if HEAP_FRAG_STATS
00424                 persistentheap->setDeallocated (0, sb->getNumBlocks() * sizeFromClass(sb->getBlockSizeClass()));
00425 #endif
00426                 sb->superblock::~superblock();
00427                 hoardFreeMemory (sb);
00428                 return 1;
00429         }
00430 
00431         // If the fullness value has changed, move the superblock.
00432         if (newFullness != oldFullness) {
00433                 moveSuperblock (sb, sizeclass, oldFullness, newFullness);
00434         } else {
00435                 // Move the superblock to the front of its list (to reduce paging).
00436                 superblock *& head = _superblocks[newFullness][sizeclass];
00437                 if (sb != head) {
00438                         sb->remove();
00439                         sb->insertBefore (head);
00440                         head = sb;
00441                 }
00442         }
00443  
00444         // If the superblock is now empty, recycle it or free it.
00445 
00446         if ((newFullness == 0) &&
00447             (sb->getNumBlocks() == sb->getNumAvailable())) 
00448         {
00449                 removeSuperblock (sb, sizeclass);
00450                 if (_numProcessors == 1) {
00451                         assert(0);
00452                         hoardFreeMemory (sb);
00453                 } else {
00454                         recycle (sb);
00455                         // Update the stats.  This restores the stats to their state
00456                         // before the call to removeSuperblock, above.
00457                         incStats (sizeclass, 0, sb->getNumBlocks());
00458                 }
00459         }
00460 
00461         // If this is the persistent heap, then we're done.
00462         if (this == (hoardHeap *) persistentheap) {
00463                 return 0;
00464         }
00465 
00466         //FIXME: What is the right policy here for persistent blocks?
00467         //TODO: What is the right policy here for persistent blocks?
00468 
00469         //
00470         // Release a superblock, if necessary.
00471         //
00472 
00473         //
00474         // Check to see if the amount free exceeds the release threshold
00475         // (two superblocks worth of blocks for a given sizeclass) and if
00476         // the heap is sufficiently empty.
00477         //
00478 
00479         // We never move anything to the process heap if we're on a
00480         // uniprocessor.
00481         if (_numProcessors > 1) {
00482                 int inUse, allocated;
00483                 getStats (sizeclass, inUse, allocated);
00484                 const bool crossedThreshold 
00485                                = ((inUse < allocated - getReleaseThreshold(sizeclass)) && 
00486                                   (EMPTY_FRACTION * inUse < EMPTY_FRACTION * allocated - allocated));
00487                 if (crossedThreshold) {
00488                         // We've crossed the magical threshold. Find the superblock with
00489                         // the most free blocks and give it to the process heap.
00490                         superblock * const maxSb = removeMaxSuperblock (sizeclass);
00491                         assert (maxSb != NULL);
00492 
00493                         // Update the statistics.
00494 
00495                         assert (maxSb->getNumBlocks() >= maxSb->getNumAvailable());
00496 
00497                         // Give the superblock back to the process heap.
00498                         persistentheap->release (maxSb);
00499         }
00500         }
00501 
00502         return 0;
00503 }
00504 
00505 // Static initialization of the number of processors (and a mask).
00506    
00507 int hoardHeap::_numProcessors;
00508 int hoardHeap::_numProcessorsMask;
00509 
00510 hoardHeap::_initNumProcs::_initNumProcs(void)
00511 {
00512   hoardHeap::_numProcessors = hoardGetNumProcessors();
00513   hoardHeap::_numProcessorsMask = (1 << (lg(hoardGetNumProcessors()) + 4)) - 1;
00514   if (hoardHeap::_numProcessors > MAX_HEAPS) {
00515           hoardHeap::_numProcessorsMask = MAX_HEAPS_MASK;
00516   }
00517 }
00518 
00519 static hoardHeap::_initNumProcs initProcs;

Generated on Sat Apr 23 11:43:35 2011 for Mnemosyne by  doxygen 1.4.7