Search code examples
linuxmemorygccglibcallocation

malloc/realloc/free capacity optimization


When you have a dynamically allocated buffer that varies its size at runtime in unpredictable ways (for example a vector or a string) one way to optimize its allocation is to only resize its backing store on powers of 2 (or some other set of boundaries/thresholds), and leave the extra space unused. This helps to amortize the cost of searching for new free memory and copying the data across, at the expense of a little extra memory use. For example the interface specification (reserve vs resize vs trim) of many C++ stl containers have such a scheme in mind.

My question is does the default implementation of the malloc/realloc/free memory manager on Linux 3.0 x86_64, GLIBC 2.13, GCC 4.6 (Ubuntu 11.10) have such an optimization?

void* p = malloc(N);
... // time passes, stuff happens
void* q = realloc(p,M);

Put another way, for what values of N and M (or in what other circumstances) will p == q?


Solution

  • From the realloc implementation in glibc trunk at http://sources.redhat.com/git/gitweb.cgi?p=glibc.git;a=blob;f=malloc/malloc.c;h=12d2211b0d6603ac27840d6f629071d1c78586fe;hb=HEAD

    First, if the memory has been obtained via mmap() instead of sbrk(), which glibc malloc does for large requests, >= 128 kB by default IIRC:

    
       if (chunk_is_mmapped(oldp))
       {
         void* newmem;
    
     #if HAVE_MREMAP
         newp = mremap_chunk(oldp, nb);
         if(newp) return chunk2mem(newp);
     #endif
         /* Note the extra SIZE_SZ overhead. */
         if(oldsize - SIZE_SZ >= nb) return oldmem; /* do nothing */
         /* Must alloc, copy, free. */
         newmem = public_mALLOc(bytes);
         if (newmem == 0) return 0; /* propagate failure */
         MALLOC_COPY(newmem, oldmem, oldsize - 2*SIZE_SZ);
         munmap_chunk(oldp);
         return newmem;
       }
    

    (Linux has mremap(), so in practice this is what is done).

    For smaller requests, a few lines below we have

    newp = _int_realloc(ar_ptr, oldp, oldsize, nb);
    

    where _int_realloc is a bit big to copy-paste here, but you'll find it starting at line 4221 in the link above. AFAICS, it does NOT do the constant factor optimization increase that e.g. the C++ std::vector does, but rather allocates exactly the amount requested by the user (rounded up to the next chunk boundaries + alignment stuff and so on).

    I suppose the idea is that if the user wants this factor of 2 size increase (or any other constant factor increase in order to guarantee logarithmic efficiency when resizing multiple times), then the user can implement it himself on top of the facility provided by the C library.