/* malloc-test.c * by Wolfram Gloger 1995, 1996 * * This program is provided `as is', there is no warranty. * https://raw.githubusercontent.com/emeryberger/Malloc-Implementations/master/allocators/CAMA/malloc-test.c */ #if !defined(__STDC__) #define __STDC__ 1 #endif #include #include #if !defined(_WIN32) #include #include #include #include #endif #ifndef MEMORY #define MEMORY 4000000l #endif #ifndef BINS_MAX #define BINS_MAX 32768 #endif #define SBINS_MAX 1024 #define SIZE 4024 #define I_MAX 5000 #ifndef I_AVERAGE #define I_AVERAGE 200 #endif #define ACTIONS_MAX 50 #ifndef SBRK_AVG #define SBRK_AVG 0 #endif #ifndef MMAP_THRESH #define MMAP_THRESH 0 #endif #ifndef TEST #define TEST 4 /* minimal testing */ #endif #ifndef TEST_INC #define TEST_INC 2047 #endif #if defined(__i386__) || defined(__sparc__) || defined(mips) || defined(_WIN32) #define PAGE_SIZE 4096 #elif defined(__alpha__) #define PAGE_SIZE 8192 #elif defined(__SVR4) #define PAGE_SIZE 8192 #else #define PAGE_SIZE 4096 /* default */ #endif #define RANDOM(s) (lran2(0) % (s)) /* All probabilities are parts in 1024. */ #ifndef PROB_MEMALIGN #define PROB_MEMALIGN 0 #endif #ifndef PROB_REALLOC #define PROB_REALLOC 48 #endif #ifndef PROB_CALLOC #define PROB_CALLOC 0 #endif struct bin { unsigned char *ptr; unsigned long size; } m[BINS_MAX], sm[SBINS_MAX]; unsigned long size = SIZE, bins=0, sbins=0; unsigned long total_size=0, total_size_max=0; unsigned char *base_ptr; unsigned long base_save; long #if __STDC__ lran2(long seed) #else lran2(seed) long seed; #endif #define LRAN2_MAX 714025l /* constants for portable */ #define IA 1366l /* random number generator */ #define IC 150889l /* (see Numerical Recipes p. 211) */ { static int first = 1; static long x, y, v[97]; int j; if(seed || first) { first = 0; x = (IC - seed) % LRAN2_MAX; if(x < 0) x = -x; for(j=0; j<97; j++) { x = (IA*x + IC) % LRAN2_MAX; v[j] = x; } x = (IA*x + IC) % LRAN2_MAX; y = x; } j = y % 97; y = v[j]; x = (IA*x + IC) % LRAN2_MAX; v[j] = x; return y; } #undef IA #undef IC void #if __STDC__ mem_init(unsigned char *ptr, unsigned long size) #else mem_init(ptr, size) unsigned char *ptr; unsigned long size; #endif { unsigned long i, j; if(size == 0) return; if(size > sizeof(unsigned long)) { /* Try the complete initial word. */ *(unsigned long *)ptr = (unsigned long)ptr ^ size; i = TEST_INC; } else i = 0; for(; i>8)) & 0xFF); } j = (unsigned long)ptr ^ (size-1); ptr[size-1] = ((j ^ (j>>8)) & 0xFF); } int #if __STDC__ mem_check(unsigned char *ptr, unsigned long size) #else mem_check(ptr, size) unsigned char *ptr; unsigned long size; #endif { unsigned long i, j; if(size == 0) return 0; if(size > sizeof(unsigned long)) { if(*(unsigned long *)ptr != ((unsigned long)ptr ^ size)) { printf ("failed size check: expected %lx, found %lx!\n", ((unsigned long) ptr ^ size), *(unsigned long *) ptr); return 1; } i = TEST_INC; } else i = 0; for(; i>8)) & 0xFF)) return 2; } j = (unsigned long)ptr ^ (size-1); if(ptr[size-1] != ((j ^ (j>>8)) & 0xFF)) { printf ("failed last byte check: expected %lx, found %x!\n", ((unsigned long) ((j ^ (j>>8)) & 0xFF)), ptr[size-1]); return 3; } return 0; } long #if __STDC__ random_size(long max) #else random_size(max) long max; #endif { long r1, r2, r, max_pages; max_pages = max/PAGE_SIZE; if(max_pages > 0) { r1 = RANDOM(1024); r2 = (r1 & 7)*4; if(r1 < 512) { /* small value near power of two */ r = (1L << (r1 >> 6)) + r2; } else if(r1 < 512+20) { /* value near a multiple of the page size */ r = (RANDOM(max_pages)+1)*PAGE_SIZE + r2 - 16; /*printf("r = %4lx\n", r);*/ } else r = RANDOM(max) + 1; } else r = RANDOM(max) + 1; /*if(r <= 0) exit(-1);*/ return r; } void #if __STDC__ bin_alloc(struct bin *m) #else bin_alloc(m) struct bin *m; #endif { long r, key; unsigned long sz; #if TEST > 0 if(mem_check(m->ptr, m->size)) { printf("bin_alloc: memory corrupt at %p, size=%lu!\n", m->ptr, m->size); exit(1); } #endif total_size -= m->size; r = RANDOM(1024); if(r < PROB_MEMALIGN) { #if !defined(_WIN32) if(m->size > 0) free(m->ptr); m->size = random_size(size); #if PROB_MEMALIGN m->ptr = (unsigned char *)memalign(4 << RANDOM(8), m->size); #endif #endif } else if(r < (PROB_MEMALIGN + PROB_REALLOC)) { if(m->size == 0) { #ifndef __sparc__ m->ptr = NULL; #else /* SunOS4 does not realloc() a NULL pointer */ m->ptr = (unsigned char *)malloc(1); #endif } #if TEST > 2 key = RANDOM(256); sz = m->size; for(r=0; rptr[r] = (r ^ key) & 0xFF; #endif m->size = random_size(size); /*printf("realloc %d\n", (int)m->size);*/ m->ptr = (unsigned char *)realloc(m->ptr, m->size); #if TEST > 2 if(m->size < sz) sz = m->size; for(r=0; rptr[r] != ((r ^ key) & 0xFF)) { printf("realloc bug !\n"); exit(1); } #endif } else if(r < (PROB_MEMALIGN + PROB_REALLOC + PROB_CALLOC)) { if(m->size > 0) free(m->ptr); m->size = random_size(size); m->ptr = (unsigned char *)calloc(m->size, 1); #if TEST > 2 for(r=0; rsize; r++) if(m->ptr[r] != '\0') { printf("calloc bug !\n"); exit(1); } #endif } else { /* normal malloc call */ if(m->size > 0) free(m->ptr); m->size = random_size(size); m->ptr = (unsigned char *)malloc(m->size); } if(!m->ptr) { printf("out of memory!\n"); exit(1); } total_size += m->size; if(total_size > total_size_max) total_size_max = total_size; #if TEST > 0 mem_init(m->ptr, m->size); #endif if(m->ptr < base_ptr) { #ifdef VERBOSE printf("hmmm, allocating below brk...\n"); #endif base_ptr = m->ptr; } } void #if __STDC__ bin_free(struct bin *m) #else bin_free(m) struct bin *m; #endif { if(m->size == 0) return; #if TEST > 0 if(mem_check(m->ptr, m->size)) { printf("bin_free: memory corrupt!\n"); exit(1); } #endif total_size -= m->size; free(m->ptr); m->size = 0; } void bin_test() { unsigned int b; int v; // printf ("bin_test.\n"); for(b=0; b= 1000000) { total_usec -= 1000000; total_sec++; } printf(" t=%ld.%06ldsec", total_sec, total_usec); #endif } int #if __STDC__ main(int argc, char *argv[]) #else main(argc, argv) int argc; char *argv[]; #endif { int i, j, next_i, count, max=I_MAX, actions; unsigned int b; long sbrk_max, sum; double sbrk_used_sum, total_size_sum; if(argc > 1) max = atoi(argv[1]); if(argc > 2) size = atoi(argv[2]); lran2((long)max ^ size); bins = (MEMORY/size)*4; if(bins > BINS_MAX) bins = BINS_MAX; #if 0 // FIX ME? Disable sbrk... base_ptr = (unsigned char *)sbrk(0); sum = (long)base_ptr % PAGE_SIZE; if(sum > 0) { if((char *)sbrk((long)PAGE_SIZE - sum) == (char *)-1) exit(1); base_ptr += (long)PAGE_SIZE - sum; /*printf("base_ptr = %lx\n", (long)base_ptr);*/ } /* attempt to fill up the region below the initial brk */ void* dummy = 0; for(i=0; i<10000; i++) { dummy = malloc(1); if(dummy >= (void*)base_ptr) break; } free(dummy); base_save = ((unsigned long)base_ptr >> 24) << 24; #endif #if MMAP_THRESH > 0 if(!mallopt(-3, MMAP_THRESH)) printf("mallopt failed!\n"); if(!mallopt(-4, 200)) printf("mallopt failed!\n"); #endif #ifdef VERBOSE printf("# mmap_thresh=%d\n", MMAP_THRESH); printf("# bins=%d max=%d size=%d\n", bins, max, size); printf("# base=%lx\n", base_save); #endif for(b=0; b 1 bin_test(); #endif #ifdef MSTATS malloc_stats(); #endif actions = RANDOM(ACTIONS_MAX); for(j=0; j 3 bin_test(); #endif } i += actions; #ifdef AFTER_FREE AFTER_FREE; #endif #if SBRK_AVG > 0 if(sbins0 && sm[sbins].ptr==(sm[sbins-1].ptr+sm[sbins-1].size)) { sm[sbins-1].size += sm[sbins].size; sbins--; } #ifdef VERBOSE printf("sbrk #%d %p %ld\n", sbins, sm[sbins].ptr, sm[sbins].size); #endif #if TEST > 0 mem_init(sm[sbins].ptr, sm[sbins].size); #endif sbins++; } #endif actions = RANDOM(ACTIONS_MAX); for(j=0; j 3 bin_test(); #endif } i += actions; if(i >= next_i) { /* gather statistics */ count++; #if !defined(_WIN32) sum = (long)sbrk(0); #else sum = 0; #endif if(sum > sbrk_max) sbrk_max = sum; sbrk_used_sum += sum; total_size_sum += (double)total_size; #ifdef VERBOSE printf("%8d %7lu\n", i, total_size); #endif next_i += I_AVERAGE; } } /* Correct sbrk values. */ sbrk_max -= (long)base_ptr; sbrk_used_sum -= (double)count*(long)base_ptr; #ifdef VERBOSE printf("# initial brk: %lx\n", (long)base_ptr); printf("# max. sbrk()'ed memory: %ld bytes\n", sbrk_max); printf("# avg. sbrk()'ed memory: %ld bytes\n", (long)(sbrk_used_sum/count)); printf("# current size allocated: %ld bytes\n", total_size); printf("# maximum size allocated: %ld bytes\n", total_size_max); printf("# average size allocated: %.1f bytes\n", total_size_sum/count); printf("# current heap waste: %.2f%%\n", (1.0 - (double)total_size_max/sbrk_max)*100.0); printf("# average heap waste: %.2f%%\n", (1.0 - (double)total_size_sum/sbrk_used_sum)*100.0); printf("# total sbrk calls performed: %d\n", sbins); #else printf("size=%7ld waste=%7.3f%%", size, /* (1.0 - (double)total_size_max/sbrk_max)*100.0, */ (1.0 - (double)total_size_sum/sbrk_used_sum)*100.0); print_times(); printf("\n"); #endif return 0; } /* testing: * gcc -Wall -Werror -fpic -march=native -mtune=native -Ofast test.c -o test * gcc -Wall -Werror -fpic -march=native -mtune=native -Ofast test.c -o test -lumem -lumem_malloc * * https://github.com/sharkdp/hyperfine * $ ldd test linux-vdso.so.1 (0x00007ffc607de000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb619a4d000) /lib64/ld-linux-x86-64.so.2 (0x00007fb619ce9000) $ ldd test_umem linux-vdso.so.1 (0x00007ffd1ff59000) libumem_malloc.so.0 => /usr/local/lib/libumem_malloc.so.0 (0x00007fe885b18000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe885926000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe885903000) libumem.so.0 => /usr/local/lib/libumem.so.0 (0x00007fe88585b000) /lib64/ld-linux-x86-64.so.2 (0x00007fe885bc7000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe885855000) $ hyperfine --warmup 3 --min-runs 10 ./test Benchmark #1: ./test Time (mean ± σ): 82.1 ms ± 1.1 ms [User: 81.0 ms, System: 0.9 ms] Range (min … max): 79.3 ms … 84.4 ms 35 runs $ hyperfine --warmup 3 --min-runs 10 ./test_umem Benchmark #1: ./test_umem Time (mean ± σ): 85.7 ms ± 1.5 ms [User: 83.2 ms, System: 2.5 ms] Range (min … max): 81.8 ms … 89.2 ms 34 runs */ /* * Local variables: * tab-width:4 * compile-command: "gcc -fpic -Wall -Werror -Ofast -march=native -mtune=native test.c -o test" * End: */