Small block configuration

You configure the small blocks by setting various bands of different sizes. Each band defines a fixed size block, and a number that describes the size of the pool for that size. The allocator initially adjusts all band sizes to be multiples of _MALLOC_ALIGN (which is 8), and then takes the size of the pools and normalizes them so that each band pool is constructed from a pool size of 4 KB.

By default, bands in the allocator are defined as:

  • _MALLOC_ALIGN × 2 = 16
  • _MALLOC_ALIGN × 3 = 24
  • _MALLOC_ALIGN × 4 = 32
  • _MALLOC_ALIGN × 6 = 48
  • _MALLOC_ALIGN × 8 = 64
  • _MALLOC_ALIGN × 10 = 80
  • _MALLOC_ALIGN × 12 = 96
  • _MALLOC_ALIGN × 16 = 128

so the smallest small block is 16 bytes, and the largest small block is 128 bytes. Allocations larger than the largest band size are serviced by the large allocator.

After initial normalization by the allocator, the band sizes and the pool sizes are adjusted to the following:

Band size Number of items
16 167
24 125
32 100
48 71
64 55
80 45
96 38
128 28

This normalization takes into account alignment restrictions and overhead needed by the allocator to manage these blocks. The number of items is the number of blocks of the given size that are created each time a new bucket is allocated.

You can specify you own band configurations by defining the following in your application's code:

typedef struct Block Block;
typedef struct Band Band;

struct Band {
  short nbpe; /* element size */
  short nalloc; /* elements per block */
  size_t slurp;
  size_t esize;
  size_t mem;
  size_t rem;
  unsigned nalloc_stats;
  Block * alist;  /* Blocks that have data to allocate */
  Block * dlist;  /* completely allocated (depleted) Blocks */
  unsigned    blk_alloced;    /* #blocks allocated */
  unsigned    blk_freed;      /* #blocks freed */
  unsigned    alloc_counter;  /* allocs */
  unsigned    free_counter;   /* frees */
  unsigned    blk_size;     /* size of allocated blocks */
};

static Band a1 = { _MALLOC_ALIGN*2, 32, 60};
static Band a2 = { _MALLOC_ALIGN*3, 32, 60};
static Band a3 = { _MALLOC_ALIGN*4, 32, 60};
static Band a4 = { _MALLOC_ALIGN*5, 24, 60};
static Band a5 = { _MALLOC_ALIGN*6, 24, 60};
static Band a6 = { _MALLOC_ALIGN*7, 24, 60};
static Band a7 = { _MALLOC_ALIGN*8, 16, 60};
static Band a8 = { _MALLOC_ALIGN*9, 8, 60};
static Band a9 = { _MALLOC_ALIGN*10, 8, 60};
static Band a10 = { _MALLOC_ALIGN*11, 8, 60};
static Band a11 = { _MALLOC_ALIGN*12, 8, 60};
static Band a12 = { _MALLOC_ALIGN*13, 8, 60};
static Band a13 = { _MALLOC_ALIGN*32, 10, 60};
Band *__dynamic_Bands[] = { &a1, &a2, &a3, &a4, &a5, &a6,
                            &a7, &a8, &a9, &a10, &a11, &a12,
                            &a13,
                          };
unsigned __dynamic_nband=13;

The main variables are *__dynamic_Bands[] and __dynamic_Bands, which specify the band configurations and the number of bands. For example, the following line:

static Band a9 = { _MALLOC_ALIGN*10, 8, 60};

specifies a band size of 80 bytes, with each chunk having at least 8 blocks, and a preallocation value of 60. The allocator first normalizes the band size to 80, and the number of items to 45. Then during initialization of the allocator, it preallocates at least 60 blocks of this size band. (Each bucket will have 45 blocks, so 60 blocks will be constructed from two buckets).

If you specify your own bands:
  • The sizes must all be distinct.
  • The band configuration must be provided in ascending order of sizes (i.e band 0 size < band 1 size < band 2 size, and so on).

A user-specified band configuration of:

static Band a1 = { 2, 32, 60};
static Band a2 = { 15, 32, 60};
static Band a3 = { 29, 32, 60};
static Band a4 = { 55, 24, 60};
static Band a5 = { 100, 24, 60};
static Band a6 = { 130, 24, 60};
static Band a7 = { 260, 8, 60};
static Band a8 = { 600, 4, 60};
Band *__dynamic_Bands[] = {&a1, &a2, &a3, &a4,
                           &a5, &a6, &a7, &a8, 
                          };
unsigned __dynamic_nband=8;

will be normalized to:

Band size Number of items
8 251
16 167
32 100
56 62
104 35
136 27
264 13
600 5

In addition to specifying the band configurations, you also have to set the environment variable:

export MALLOC_MEMORY_BANDCONFIG=1

to ensure that your configurations are picked.

For the above configuration, allocations larger than 600 bytes will be serviced by the large block allocator.

When used in conjunction with the MALLOC_MEMORY_PREALLOCATE option for the arena cache, the preallocations of blocks in bands are performed by initially populating the arena cache, and then allocating bands from this arena cache.

You can also configure the bands by using the MALLOC_BAND_CONFIG_STR environment variable. The string format is:

N:s1,n1,p1:s2,n2,p2:s3,n3,p3: ... :sN,nN,pN

where the components are:

s
The band size.
n
The number of items.
p
The preallocation value, which can be zero.

You must specify s, n, and p for each band. The string can't include any spaces; the only valid characters are digits, colons (:), and commas (,). Position is important. The parsing is simple and strict: sizes are assumed to be provided in ascending order, further validation is done by the allocator. If the allocator doesn't like the string, it ignores it completely.