/ Check-in [7d2cdfad]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Use a heap-based primary queue rather than a linked list to store the available free blocks of each size in MEMSYS5, since this provides faster access to the first available block.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | memsys5-performance
Files: files | file ages | folders
SHA1:7d2cdfad0e95f8c5b5f7e0abe3b7c0d1fa9f5b83
User & Date: drh 2013-11-23 21:30:22
Context
2013-11-23
21:30
Use a heap-based primary queue rather than a linked list to store the available free blocks of each size in MEMSYS5, since this provides faster access to the first available block. Closed-Leaf check-in: 7d2cdfad user: drh tags: memsys5-performance
21:29
Add newlines at the end of some error messages in speedtest1. check-in: 6b98f0af user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/mem5.c.

    59     59   /*
    60     60   ** A minimum allocation is an instance of the following structure.
    61     61   ** Larger allocations are an array of these structures where the
    62     62   ** size of the array is a power of 2.
    63     63   **
    64     64   ** The size of this object must be a power of two.  That fact is
    65     65   ** verified in memsys5Init().
           66  +**
           67  +** This object overlays the memory allocation when the memory is available
           68  +** to be allocated.  In other words Mem5Link objects represent memory
           69  +** that is available to be allocated.
           70  +**
           71  +** All Mem5Link objects of the same size are arranged on a dynamic
           72  +** heap, with the root being the Mem5Link that has the smallest address.
           73  +** The iLeft and iRight Mem5Link objects are children.  The iSlot numbers
           74  +** for the left and right child are as follows:
           75  +**
           76  +**      pLeft->iSlot = pParent->iSlot*2;
           77  +**      pRight->iSlot = pParent->iSlot*2 + 1;
           78  +**
           79  +** The root of the heap always has iSlot==1.  If the heap contains
           80  +** N elements, then they cover all slots from 1 through N.
    66     81   */
    67     82   typedef struct Mem5Link Mem5Link;
    68     83   struct Mem5Link {
    69         -  int next;       /* Index of next free chunk */
    70         -  int prev;       /* Index of previous free chunk */
           84  +  int iLeft;       /* left child in the heap */
           85  +  int iRight;      /* right child in the heap */
           86  +  int iSlot;       /* Slot in the heap holding this item. */
           87  +  int filler;      /* Not used. Needed to round size up to power of two */
    71     88   };
    72     89   
    73     90   /*
    74     91   ** Maximum size of any allocation is ((1<<LOGMAX)*mem5.szAtom). Since
    75     92   ** mem5.szAtom is always at least 8 and 32-bit integers are used,
    76     93   ** it is not actually possible to reach this limit.
    77     94   */
................................................................................
    83    100   #define CTRL_LOGSIZE  0x1f    /* Log2 Size of this block */
    84    101   #define CTRL_FREE     0x20    /* True if not checked out */
    85    102   
    86    103   /*
    87    104   ** All of the static variables used by this module are collected
    88    105   ** into a single structure named "mem5".  This is to keep the
    89    106   ** static variables organized and to reduce namespace pollution
    90         -** when this module is combined with other in the amalgamation.
          107  +** when this module is combined with others in the amalgamation.
    91    108   */
    92    109   static SQLITE_WSD struct Mem5Global {
    93         -  /*
    94         -  ** Memory available for allocation
          110  +  /* Memory available for allocation
    95    111     */
    96    112     int szAtom;      /* Smallest possible allocation in bytes */
    97    113     int nBlock;      /* Number of szAtom sized blocks in zPool */
    98    114     u8 *zPool;       /* Memory available to be allocated */
    99    115     
   100         -  /*
   101         -  ** Mutex to control access to the memory allocation subsystem.
          116  +  /* Mutex to control access to the memory allocation subsystem.
   102    117     */
   103    118     sqlite3_mutex *mutex;
   104    119   
   105         -  /*
   106         -  ** Performance statistics
          120  +  /* Performance statistics
   107    121     */
   108    122     u64 nAlloc;         /* Total number of calls to malloc */
   109    123     u64 totalAlloc;     /* Total of all malloc calls - includes internal frag */
   110    124     u64 totalExcess;    /* Total internal fragmentation */
   111    125     u32 currentOut;     /* Current checkout, including internal fragmentation */
   112    126     u32 currentCount;   /* Current number of distinct checkouts */
   113    127     u32 maxOut;         /* Maximum instantaneous currentOut */
   114    128     u32 maxCount;       /* Maximum instantaneous currentCount */
   115    129     u32 maxRequest;     /* Largest allocation (exclusive of internal frag) */
   116    130     
   117         -  /*
   118         -  ** Lists of free blocks.  aiFreelist[0] is a list of free blocks of
   119         -  ** size mem5.szAtom.  aiFreelist[1] holds blocks of size szAtom*2.
   120         -  ** and so forth.
          131  +  /* Heaps of free blocks.  aHeap[0].iRoot is the root of a dynamic
          132  +  ** min-heap of free blocks of size mem5.szAtom.  aHeap[1].iRoot is
          133  +  ** a heap of free blocks of size mem5.szAtom*2.  aHeap[x].iRoot is
          134  +  ** free heap of blocks of size mem5.szAtom*(1<<x).  aHeap[x].nLink
          135  +  ** the number of free blocks on each heap.
          136  +  */
          137  +  struct Mem5Heap {
          138  +    int iRoot;            /* The root Mem5Link for this heap */
          139  +    int nLink;            /* Number of Mem5Link objects on this heap */
          140  +  } aHeap[LOGMAX+1];
          141  +
          142  +  /* The memsys5HeapPath() routine finds a sequence of nodes in a heap
          143  +  ** going to a particular Mem5Link.  It writes those nodes into the
          144  +  ** following array.  The root of the tree is in aPath[0].  The first
          145  +  ** child in aPath[1].  And so forth.
   121    146     */
   122         -  int aiFreelist[LOGMAX+1];
          147  +  int aPath[29];          /* Path to a node in a heap */
   123    148   
   124    149     /*
   125    150     ** Space for tracking which blocks are checked out and the size
   126    151     ** of each block.  One byte per block.
   127    152     */
   128    153     u8 *aCtrl;
   129    154   
................................................................................
   134    159   */
   135    160   #define mem5 GLOBAL(struct Mem5Global, mem5)
   136    161   
   137    162   /*
   138    163   ** Assuming mem5.zPool is divided up into an array of Mem5Link
   139    164   ** structures, return a pointer to the idx-th such link.
   140    165   */
   141         -#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom]))
   142         -
   143         -/*
   144         -** Unlink the chunk at mem5.aPool[i] from list it is currently
   145         -** on.  It should be found on mem5.aiFreelist[iLogsize].
   146         -*/
   147         -static void memsys5Unlink(int i, int iLogsize){
   148         -  int next, prev;
   149         -  assert( i>=0 && i<mem5.nBlock );
   150         -  assert( iLogsize>=0 && iLogsize<=LOGMAX );
   151         -  assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
   152         -
   153         -  next = MEM5LINK(i)->next;
   154         -  prev = MEM5LINK(i)->prev;
   155         -  if( prev<0 ){
   156         -    mem5.aiFreelist[iLogsize] = next;
          166  +#define MEM5_PTR(idx) ((Mem5Link*)(&mem5.zPool[(idx)*mem5.szAtom]))
          167  +
          168  +/*
          169  +** Given a pointer to an Mem5Link object, return its index into the
          170  +** array of all Mem5Link objects.
          171  +*/
          172  +#define MEM5_ID(p)    (int)(((u8*)(p)-mem5.zPool)/mem5.szAtom)
          173  +
          174  +#if 0  /* This validation step is too slow for normal use */
          175  +/*
          176  +** Validate an Mem5Link object to make sure it satisfies all of the
          177  +** dynamic heap properties.
          178  +*/
          179  +static void memsys5Validate(int iLink, int nLink){
          180  +  Mem5Link *p = MEM5_PTR(iLink);
          181  +  assert( p->iLeft>=0 || p->iRight<0 );
          182  +  assert( p->iSlot<=nLink );
          183  +  if( p->iLeft>=0 ){
          184  +    assert( p->iLeft>iLink );
          185  +    assert( p->iLeft!=p->iRight );
          186  +    assert( MEM5_PTR(p->iLeft)->iSlot==p->iSlot*2 );
          187  +    memsys5Validate(p->iLeft, nLink);
          188  +  }else{
          189  +    assert( p->iSlot*2>nLink );
          190  +  }
          191  +  if( p->iRight>=0 ){
          192  +    assert( p->iRight>iLink );
          193  +    assert( p->iLeft!=p->iRight );
          194  +    assert( MEM5_PTR(p->iRight)->iSlot==p->iSlot*2+1 );
          195  +    memsys5Validate(p->iRight, nLink);
          196  +  }else{
          197  +    assert( p->iSlot*2+1>nLink );
          198  +  }
          199  +}
          200  +#else
          201  +# define memsys5Validate(X,Y)
          202  +#endif
          203  +
          204  +/* 
          205  +** Compute a path from the root of a heap to a particular slot of the
          206  +** heap.  The root is node iRoot and the desired slot is iSlot.  Write
          207  +** the sequence of Mem5Link objects on this path into mem5.aPath[] and
          208  +** return the number of elements on the path.
          209  +*/
          210  +static int memsys5HeapPath(int iRoot, int iSlot){
          211  +  int x = 1;
          212  +  int i = 0;
          213  +  int iRes = iRoot;;
          214  +
          215  +  while( x<=iSlot ) x <<= 1;
          216  +  x >>= 2;
          217  +  mem5.aPath[0] = iRoot;
          218  +  assert( MEM5_PTR(iRoot)->iSlot==1 );
          219  +  while( x ){
          220  +    Mem5Link *p = MEM5_PTR(iRes);
          221  +    iRes = (x & iSlot)!=0 ? p->iRight : p->iLeft;
          222  +    mem5.aPath[++i] = iRes;
          223  +    x >>= 1;
          224  +  }
          225  +  assert( MEM5_PTR(mem5.aPath[i])->iSlot==iSlot );
          226  +  return i;
          227  +}
          228  +
          229  +/* One of the children of heap node iParent will be iOld.  Change
          230  +** that child to iNew.
          231  +*/
          232  +static void memsys5ChangeChild(int iParent, int iOld, int iNew){
          233  +  Mem5Link *pParent = MEM5_PTR(iParent);
          234  +  if( pParent->iLeft==iOld ){
          235  +    pParent->iLeft = iNew;
          236  +  }else{
          237  +    assert( pParent->iRight==iOld );
          238  +    pParent->iRight = iNew;
          239  +  }
          240  +}
          241  +
          242  +/*
          243  +** Swap heap elements mem5.aPath[i] and mem5.aPath[i+1].
          244  +*/
          245  +static void memsys5HeapPathSwap(int i){
          246  +  int iA = mem5.aPath[i];
          247  +  int iB = mem5.aPath[i+1];
          248  +  Mem5Link *pA = MEM5_PTR(iA);
          249  +  Mem5Link *pB = MEM5_PTR(iB);
          250  +  int iLeft, iRight, iSlot;
          251  +
          252  +  if( i>0 ) memsys5ChangeChild(mem5.aPath[i-1], iA, iB);
          253  +  if( pA->iLeft==iB ){
          254  +    iRight = pA->iRight;
          255  +    iLeft = iA;
   157    256     }else{
   158         -    MEM5LINK(prev)->next = next;
   159         -  }
   160         -  if( next>=0 ){
   161         -    MEM5LINK(next)->prev = prev;
   162         -  }
          257  +    iRight = iA;
          258  +    iLeft = pA->iLeft;
          259  +  }
          260  +  iSlot = pA->iSlot;
          261  +  pA->iRight = pB->iRight;
          262  +  pA->iLeft = pB->iLeft;
          263  +  pA->iSlot = pB->iSlot;
          264  +  pB->iRight = iRight;
          265  +  pB->iLeft = iLeft;
          266  +  pB->iSlot = iSlot;
          267  +  mem5.aPath[i] = iB;
          268  +  mem5.aPath[i+1] = iA;
          269  +}
          270  +
          271  +/*
          272  +** If mem5.aPath[i] is greater than either child, then shift
          273  +** that element downward in the heap. 
          274  +*/
          275  +static void memsys5SiftDown(int i){
          276  +  while(1){
          277  +    int x = mem5.aPath[i];
          278  +    Mem5Link *p = MEM5_PTR(x);
          279  +    if( p->iRight>=0 && p->iRight<x && p->iRight<p->iLeft ){
          280  +      mem5.aPath[i+1] = p->iRight;
          281  +    }else if( p->iLeft>=0 && p->iLeft<x ){
          282  +      mem5.aPath[i+1] = p->iLeft;
          283  +    }else{
          284  +      break;
          285  +    }
          286  +    memsys5HeapPathSwap(i);
          287  +    i++; 
          288  +  }
          289  +}
          290  +
          291  +static void memsys5SiftUp(int i){
          292  +  while( i>0 && mem5.aPath[i]<mem5.aPath[i-1] ){
          293  +    memsys5HeapPathSwap(i-1);
          294  +    i--;
          295  +  }
          296  +}
          297  +
          298  +/*
          299  +** Unlink the chunk at mem5.aPool[i] from iLogsize heap.
          300  +*/
          301  +static void memsys5Unlink(int iOld, int iLogsize){
          302  +  struct Mem5Heap *pHeap;
          303  +  Mem5Link *pOld, *pLast;
          304  +  int i, iLast;
          305  +  assert( iOld>=0 && iOld<mem5.nBlock );
          306  +  assert( iLogsize>=0 && iLogsize<=LOGMAX );
          307  +  assert( (mem5.aCtrl[iOld] & CTRL_LOGSIZE)==iLogsize );
          308  +  assert( mem5.aHeap[iLogsize].nLink>0 );
          309  +
          310  +  pHeap = &mem5.aHeap[iLogsize];
          311  +  if( pHeap->nLink==1 ){
          312  +    pHeap->nLink = 0;
          313  +    pHeap->iRoot = -1;
          314  +    return;
          315  +  }
          316  +  i = memsys5HeapPath(pHeap->iRoot, pHeap->nLink);
          317  +  iLast = mem5.aPath[i];
          318  +  assert( i>0 );
          319  +  memsys5ChangeChild(mem5.aPath[i-1], iLast, -1);
          320  +  pHeap->nLink--;
          321  +  if( iLast==iOld ) return;
          322  +  pOld = MEM5_PTR(iOld);
          323  +  pLast = MEM5_PTR(iLast);
          324  +  i = memsys5HeapPath(pHeap->iRoot, pOld->iSlot);
          325  +  assert( mem5.aPath[i]==iOld );
          326  +  pLast->iRight = pOld->iRight;
          327  +  pLast->iLeft = pOld->iLeft;
          328  +  pLast->iSlot = pOld->iSlot;
          329  +  mem5.aPath[i] = iLast;
          330  +  if( i>0 ) memsys5ChangeChild(mem5.aPath[i-1], iOld, iLast);
          331  +  memsys5SiftUp(i);
          332  +  memsys5SiftDown(i);
          333  +  pHeap->iRoot = mem5.aPath[0];
          334  +  memsys5Validate(pHeap->iRoot, pHeap->nLink);
          335  +}
          336  +
          337  +/*
          338  +** Find the first entry on the free-heap iLogsize.  Unlink that
          339  +** entry and return its index. 
          340  +*/
          341  +static int memsys5UnlinkFirst(int iLogsize){
          342  +  struct Mem5Heap *pHeap;
          343  +  int iFirst, i, iLast;
          344  +  Mem5Link *pLast;
          345  +  Mem5Link *pFirst;
          346  +
          347  +  assert( iLogsize>=0 && iLogsize<=LOGMAX );
          348  +  pHeap = &mem5.aHeap[iLogsize];
          349  +  assert( pHeap->nLink>0 );
          350  +  iFirst = pHeap->iRoot;
          351  +  pFirst = MEM5_PTR(iFirst);
          352  +  if( pHeap->nLink==1 ){
          353  +    pHeap->nLink = 0;
          354  +    return iFirst;
          355  +  }
          356  +  i = memsys5HeapPath(iFirst, pHeap->nLink);
          357  +  pHeap->nLink--;
          358  +  iLast = mem5.aPath[i];
          359  +  assert( i>0 );
          360  +  memsys5ChangeChild(mem5.aPath[i-1], iLast, -1);
          361  +  pLast = MEM5_PTR(iLast);
          362  +  pLast->iSlot = 1;
          363  +  pLast->iRight = pFirst->iRight;
          364  +  pLast->iLeft = pFirst->iLeft;
          365  +  mem5.aPath[0] = iLast;
          366  +  memsys5SiftDown(0);
          367  +  pHeap->iRoot = mem5.aPath[0];
          368  +  memsys5Validate(pHeap->iRoot, pHeap->nLink);
          369  +  return iFirst;   
   163    370   }
   164    371   
   165    372   /*
   166    373   ** Link the chunk at mem5.aPool[i] so that is on the iLogsize
   167         -** free list.
          374  +** free-block heap.
   168    375   */
   169         -static void memsys5Link(int i, int iLogsize){
   170         -  int x;
          376  +static void memsys5Link(int iNew, int iLogsize){
          377  +  struct Mem5Heap *pHeap;
          378  +  Mem5Link *pNew;
          379  +  int i;
   171    380     assert( sqlite3_mutex_held(mem5.mutex) );
   172         -  assert( i>=0 && i<mem5.nBlock );
          381  +  assert( iNew>=0 && iNew<mem5.nBlock );
   173    382     assert( iLogsize>=0 && iLogsize<=LOGMAX );
   174         -  assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
   175         -
   176         -  x = MEM5LINK(i)->next = mem5.aiFreelist[iLogsize];
   177         -  MEM5LINK(i)->prev = -1;
   178         -  if( x>=0 ){
   179         -    assert( x<mem5.nBlock );
   180         -    MEM5LINK(x)->prev = i;
          383  +  assert( (mem5.aCtrl[iNew] & CTRL_LOGSIZE)==iLogsize );
          384  +  
          385  +  pNew = MEM5_PTR(iNew);
          386  +  pHeap = &mem5.aHeap[iLogsize];
          387  +  pNew->iLeft = -1;
          388  +  pNew->iRight = -1;
          389  +  pNew->iSlot = ++pHeap->nLink;
          390  +  if( pHeap->nLink==1 ){
          391  +    pHeap->iRoot = iNew;
          392  +    return;
   181    393     }
   182         -  mem5.aiFreelist[iLogsize] = i;
          394  +  i = memsys5HeapPath(pHeap->iRoot, pHeap->nLink/2);
          395  +  memsys5ChangeChild(mem5.aPath[i], -1, iNew);
          396  +  mem5.aPath[i+1] = iNew;
          397  +  memsys5SiftUp(i+1);
          398  +  pHeap->iRoot = mem5.aPath[0];
          399  +  memsys5Validate(pHeap->iRoot, pHeap->nLink);
   183    400   }
   184    401   
   185    402   /*
   186    403   ** If the STATIC_MEM mutex is not already held, obtain it now. The mutex
   187    404   ** will already be held (obtained by code in malloc.c) if
   188    405   ** sqlite3GlobalConfig.bMemStat is true.
   189    406   */
................................................................................
   198    415   ** Return the size of an outstanding allocation, in bytes.  The
   199    416   ** size returned omits the 8-byte header overhead.  This only
   200    417   ** works for chunks that are currently checked out.
   201    418   */
   202    419   static int memsys5Size(void *p){
   203    420     int iSize = 0;
   204    421     if( p ){
   205         -    int i = ((u8 *)p-mem5.zPool)/mem5.szAtom;
          422  +    int i = MEM5_ID(p);
   206    423       assert( i>=0 && i<mem5.nBlock );
   207    424       iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
   208    425     }
   209    426     return iSize;
   210    427   }
   211    428   
   212         -/*
   213         -** Find the first entry on the freelist iLogsize.  Unlink that
   214         -** entry and return its index. 
   215         -*/
   216         -static int memsys5UnlinkFirst(int iLogsize){
   217         -  int i;
   218         -  int iFirst;
   219         -
   220         -  assert( iLogsize>=0 && iLogsize<=LOGMAX );
   221         -  i = iFirst = mem5.aiFreelist[iLogsize];
   222         -  assert( iFirst>=0 );
   223         -  while( i>0 ){
   224         -    if( i<iFirst ) iFirst = i;
   225         -    i = MEM5LINK(i)->next;
   226         -  }
   227         -  memsys5Unlink(iFirst, iLogsize);
   228         -  return iFirst;
   229         -}
   230         -
   231    429   /*
   232    430   ** Return a block of memory of at least nBytes in size.
   233    431   ** Return NULL if unable.  Return NULL if nBytes==0.
   234    432   **
   235    433   ** The caller guarantees that nByte is positive.
   236    434   **
   237    435   ** The caller has obtained a mutex prior to invoking this
   238    436   ** routine so there is never any chance that two or more
   239    437   ** threads can be in this routine at the same time.
   240    438   */
   241    439   static void *memsys5MallocUnsafe(int nByte){
   242    440     int i;           /* Index of a mem5.aPool[] slot */
   243         -  int iBin;        /* Index into mem5.aiFreelist[] */
          441  +  int iBin;        /* Index into mem5.aHeap[] */
   244    442     int iFullSz;     /* Size of allocation rounded up to power of 2 */
   245    443     int iLogsize;    /* Log2 of iFullSz/POW2_MIN */
   246    444   
   247    445     /* nByte must be a positive */
   248    446     assert( nByte>0 );
   249    447   
   250    448     /* Keep track of the maximum allocation request.  Even unfulfilled
................................................................................
   259    457     if( nByte > 0x40000000 ){
   260    458       return 0;
   261    459     }
   262    460   
   263    461     /* Round nByte up to the next valid power of two */
   264    462     for(iFullSz=mem5.szAtom, iLogsize=0; iFullSz<nByte; iFullSz *= 2, iLogsize++){}
   265    463   
   266         -  /* Make sure mem5.aiFreelist[iLogsize] contains at least one free
          464  +  /* Make sure mem5.aHeap[iLogsize] contains at least one free
   267    465     ** block.  If not, then split a block of the next larger power of
   268    466     ** two in order to create a new free block of size iLogsize.
   269    467     */
   270         -  for(iBin=iLogsize; mem5.aiFreelist[iBin]<0 && iBin<=LOGMAX; iBin++){}
          468  +  for(iBin=iLogsize; mem5.aHeap[iBin].nLink==0 && iBin<=LOGMAX; iBin++){}
   271    469     if( iBin>LOGMAX ){
   272    470       testcase( sqlite3GlobalConfig.xLog!=0 );
   273    471       sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte);
   274    472       return 0;
   275    473     }
   276    474     i = memsys5UnlinkFirst(iBin);
   277    475     while( iBin>iLogsize ){
................................................................................
   303    501   static void memsys5FreeUnsafe(void *pOld){
   304    502     u32 size, iLogsize;
   305    503     int iBlock;
   306    504   
   307    505     /* Set iBlock to the index of the block pointed to by pOld in 
   308    506     ** the array of mem5.szAtom byte blocks pointed to by mem5.zPool.
   309    507     */
   310         -  iBlock = ((u8 *)pOld-mem5.zPool)/mem5.szAtom;
          508  +  iBlock = MEM5_ID(pOld);
   311    509   
   312    510     /* Check that the pointer pOld points to a valid, non-free block. */
   313    511     assert( iBlock>=0 && iBlock<mem5.nBlock );
   314    512     assert( ((u8 *)pOld-mem5.zPool)%mem5.szAtom==0 );
   315    513     assert( (mem5.aCtrl[iBlock] & CTRL_FREE)==0 );
   316    514   
   317    515     iLogsize = mem5.aCtrl[iBlock] & CTRL_LOGSIZE;
................................................................................
   481    679     }
   482    680   
   483    681     mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8)));
   484    682     mem5.zPool = zByte;
   485    683     mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.szAtom];
   486    684   
   487    685     for(ii=0; ii<=LOGMAX; ii++){
   488         -    mem5.aiFreelist[ii] = -1;
          686  +    mem5.aHeap[ii].nLink = 0;
   489    687     }
   490    688   
   491    689     iOffset = 0;
   492    690     for(ii=LOGMAX; ii>=0; ii--){
   493    691       int nAlloc = (1<<ii);
   494    692       if( (iOffset+nAlloc)<=mem5.nBlock ){
   495    693         mem5.aCtrl[iOffset] = ii | CTRL_FREE;
................................................................................
   519    717   #ifdef SQLITE_TEST
   520    718   /*
   521    719   ** Open the file indicated and write a log of all unfreed memory 
   522    720   ** allocations into that log.
   523    721   */
   524    722   void sqlite3Memsys5Dump(const char *zFilename){
   525    723     FILE *out;
   526         -  int i, j, n;
          724  +  int i;
   527    725     int nMinLog;
   528    726   
   529    727     if( zFilename==0 || zFilename[0]==0 ){
   530    728       out = stdout;
   531    729     }else{
   532    730       out = fopen(zFilename, "w");
   533    731       if( out==0 ){
................................................................................
   535    733                         zFilename);
   536    734         return;
   537    735       }
   538    736     }
   539    737     memsys5Enter();
   540    738     nMinLog = memsys5Log(mem5.szAtom);
   541    739     for(i=0; i<=LOGMAX && i+nMinLog<32; i++){
   542         -    for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){}
   543         -    fprintf(out, "freelist items of size %d: %d\n", mem5.szAtom << i, n);
          740  +    fprintf(out, "freelist items of size %d: %d\n",
          741  +            mem5.szAtom << i, mem5.aHeap[i].nLink);
   544    742     }
   545    743     fprintf(out, "mem5.nAlloc       = %llu\n", mem5.nAlloc);
   546    744     fprintf(out, "mem5.totalAlloc   = %llu\n", mem5.totalAlloc);
   547    745     fprintf(out, "mem5.totalExcess  = %llu\n", mem5.totalExcess);
   548    746     fprintf(out, "mem5.currentOut   = %u\n", mem5.currentOut);
   549    747     fprintf(out, "mem5.currentCount = %u\n", mem5.currentCount);
   550    748     fprintf(out, "mem5.maxOut       = %u\n", mem5.maxOut);