#include #include #include "memory.h" #include "templates.h" #include "freelist.h" typedef struct chunk chunk; struct chunk { chunk *next; /* only used when in free_lists */ }; #define GRAN (sizeof (struct chunk)) #define ALLOC_SIZE (1 << 20) #define MAX_CHUNK_SIZE 256 #define NUM_BLOCK_TYPES (MAX_CHUNK_SIZE / GRAN) #ifdef MDEBUG1 #define MAX_POOLS 100 #else #define MAX_POOLS 1 #endif #ifdef MDEBUG1 static void freesize(void *p, size_t s) { (void)s; free(p); } static void die(char *blah) { perror(blah); abort(); } LIST(alloclist, chunk *); LIST_IMPL(alloclist, chunk *, KEEP(chunk *), malloc, freesize); void print_memblock_summary2(int size); #endif struct chunkpool { chunk *ch; MDEBUG1_ONLY( int pool_id; ) MDEBUG1_ONLY( alloclist *all; ) }; static struct chunkpool free_lists[NUM_BLOCK_TYPES][MAX_POOLS]; #ifdef MDEBUG1 static int total[NUM_BLOCK_TYPES][MAX_POOLS]; static int used[NUM_BLOCK_TYPES][MAX_POOLS]; static int allocs[NUM_BLOCK_TYPES][MAX_POOLS]; static int total_mallocs = 0; static int total_alloc = 0; #endif void *block_malloc(size_t size) { return block_malloc2(size, -1); } void *block_malloc2(size_t size, int pool_id) { chunk **fl = NULL; void *result; int granmult; int pool = 0; if (size > MAX_CHUNK_SIZE || size % GRAN != 0) { MDEBUG1_ONLY( total_mallocs++; ) return malloc(size); } granmult = size / GRAN; #ifdef MDEBUG1 for (pool = 0; pool + 1 < MAX_POOLS; pool++) { if (free_lists[granmult - 1][pool].pool_id == 0) { free_lists[granmult - 1][pool].pool_id = pool_id; } if (free_lists[granmult - 1][pool].pool_id == pool_id) { break; } } #endif fl = &free_lists[granmult - 1][pool].ch; if (*fl == NULL) { chunk *new_block = malloc(ALLOC_SIZE); chunk *p; MDEBUG1_ONLY( int old_size = total[granmult-1][pool]; ) if (!new_block) return NULL; MDEBUG1_ONLY( insert_alloclist(&free_lists[granmult - 1][pool].all, new_block); ) for (p = new_block; (char*)(p + granmult) <= ((char*)new_block) + ALLOC_SIZE; p += granmult) { /* each iteration adds a new chunk to the list */ MDEBUG1_ONLY( total[granmult-1][pool]++; ) *fl = p; fl = &p->next; } *fl = NULL; fl = &free_lists[granmult - 1][pool].ch; MDEBUG1_ONLY( assert((total[granmult-1][pool]-old_size)*size <= ALLOC_SIZE); ) MDEBUG1_ONLY( assert(total[granmult-1][pool]*(int)size - old_size > ALLOC_SIZE - (int) size); ) #ifdef MDEBUG1 // print some info MDEBUG2_ONLY( fprintf(stderr, "ALLOC: for size %2ld (%d:line %d), %4ld B of %4ld B used, total alloced is %8ld KiB\n", (long int) size, pool, pool_id, (long int) used[granmult-1][pool] * size, (long int) total[granmult-1][pool] * size, (long int) total_alloc / 1024); ) assert( used[granmult-1][pool] <= (signed long) total[granmult-1][pool] ); total_alloc += ALLOC_SIZE; #endif } #ifdef MDEBUG1 { static unsigned long cnt = 0, cnt2 = 0; if (++cnt % (1L << 20) == 0) { if (++cnt2 % 10 == 0) { print_memblock_summary2(0); } else { print_memblock_summary(); } } } #endif MDEBUG1_ONLY( used[granmult-1][pool]++; ) MDEBUG1_ONLY( allocs[granmult-1][pool]++; ) result = *fl; *fl = (*fl)->next; *(int *)result = ~0; return result; } #ifdef MDEBUG1 static int find_closest(void *vmem, size_t size, chunk **ch, int *p) { int pool; *ch = NULL; for (pool = 0; pool < MAX_POOLS; pool++) { alloclist *a; if (!free_lists[size/GRAN - 1][pool].all) break; for (a = free_lists[size/GRAN - 1][pool].all; a; a = a->next) { if (*ch < a->value && a->value <= (chunk*)vmem) { *ch = a->value; *p = pool; } } } assert((char*)*ch <= (char*)vmem); if ((char*)vmem - (char*)*ch < ALLOC_SIZE) { return 1; } else { return 0; } } #endif void block_free(void *vmem, size_t size) { int pool = 0; if (size > MAX_CHUNK_SIZE || size % GRAN != 0) { free(vmem); return; } #if MDEBUG1 { chunk *closest; if (!find_closest(vmem, size, &closest, &pool)) { fprintf(stderr, "AIEE: %p + %lx < %p\n", closest, (unsigned long) ALLOC_SIZE, vmem); assert(0); } } #endif MDEBUG1_ONLY( used[size/GRAN-1][pool]--; ) { chunk **fl, *x; fl = &free_lists[size/GRAN - 1][pool].ch; x = (chunk *) vmem; x->next = *fl; *fl = x; } } #ifdef MDEBUG1 void print_memblock_summary(void) { print_memblock_summary2(5*1024*1024); } void print_memblock_summary2(int size) { unsigned int i, j; fprintf(stderr, "MEMORY SUMMARY:\n"); for (i = 0; i < NUM_BLOCK_TYPES; i++) { for (j = 0; j < MAX_POOLS; j++) { if (total[i][j] * GRAN * (i+1) < size) continue; if (free_lists[i][j].all != NULL) { fprintf(stderr, " pool %dB/%d:%d; %d used %d allocated (%0.1f%% of %d MiB, %0.2f%% current)\n", (i+1) * GRAN, j, free_lists[i][j].pool_id, used[i][j], total[i][j], (100.0 * used[i][j]) / total[i][j], total[i][j] * GRAN * (i+1) / 1024 / 1024, (100.0 * used[i][j]) / allocs[i][j]); } } } } #endif