diff --git a/src/benchmarks/t_test1.py b/src/benchmarks/t_test1.py new file mode 100644 index 0000000000000000000000000000000000000000..e43a511befd3b72ca5714ae85cbd73fa5fe35dea --- /dev/null +++ b/src/benchmarks/t_test1.py @@ -0,0 +1,53 @@ +from src.allocator import bumpptr +from src.benchmark import Benchmark + + +class Benchmark_t_test1(Benchmark): + def __init__(self): + self.name = "t_test1" + self.descrition = """This benchmark from ptmalloc2 allocates and frees + n bins in t concurrent threads.""" + + self.cmd = "t-test1 {nthreads} {nthreads} 1000000 {maxsize}" + + self.args = {"maxsize": [2 ** x for x in range(6, 18)], + "nthreads": Benchmark.scale_threads_for_cpus(2)} + + self.requirements = ["t-test1"] + super().__init__() + + self.allocators["bumpptr"] = bumpptr.build() + + def summary(self): + # mops / per second + yval = "perm.nthreads / ({task-clock}/1000)" + # Speed + self.plot_fixed_arg(yval, + ylabel='"Mops / CPU second"', + title='"T-Ttest1: " + arg + " " + str(arg_value)', + filepostfix="time", + autoticks=False) + + scale = list(self.results["allocators"].keys())[0] + self.plot_fixed_arg(yval, + ylabel='"Mops / CPU second scaled at {}"'.format(scale), + title='"T-Test1: " + arg + " " + str(arg_value) + " normalized {}"'.format(scale), + filepostfix="time.norm", + scale=scale, + autoticks=False) + + # L1 cache misses + self.plot_fixed_arg("({L1-dcache-load-misses}/{L1-dcache-loads})*100", + ylabel='"L1 misses in %"', + title='"T-Test1 l1 cache misses: " + arg + " " + str(arg_value)', + filepostfix="l1misses", + autoticks=False) + + # Speed Matrix + self.write_best_doublearg_tex_table(yval, + filepostfix="memusage.matrix") + + self.export_to_csv("task-clock") + + +t_test1 = Benchmark_t_test1() diff --git a/src/benchmarks/t_test1/Makefile b/src/benchmarks/t_test1/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..109e9a607ac3ca4ca9acedade5b651d195556e8e --- /dev/null +++ b/src/benchmarks/t_test1/Makefile @@ -0,0 +1,37 @@ +# Makefile for t-test1 from ptmalloc, version 2 +# by Florian Fischer 2019 +# derived from +# Makefile for ptmalloc, version 2 +# by Wolfram Gloger 1996-1999, 2001, 2002, 2003, 2004, 2006 + +OBJDIR ?= obj + +CC ?= cc + +SYS_FLAGS = -D_GNU_SOURCE=1 +OPT_FLAGS ?= -g -O # -O2 +WARN_FLAGS ?= -Wall -Wstrict-prototypes + +# Flags for the test programs +T_FLAGS = -DTEST=1 + +# Thread flags. +# See the platform-specific targets below. +THR_FLAGS = -DUSE_TSD_DATA_HACK -D_REENTRANT +THR_LIBS = -lpthread + +RM = rm -f + +CFLAGS ?= $(SYS_FLAGS) $(OPT_FLAGS) $(WARN_FLAGS) $(THR_FLAGS) + +all: $(OBJDIR)/t-test1 + +$(OBJDIR)/t-test1: t-test1.c t-test.h | $(OBJDIR) + @echo compiling $@ + $(CC) $(CFLAGS) $(T_FLAGS) t-test1.c $(THR_LIBS) -o $@ + +$(OBJDIR): + mkdir -p $@ + +clean: + $(RM) -rf $(OBJDIR) diff --git a/src/benchmarks/t_test1/lran2.h b/src/benchmarks/t_test1/lran2.h new file mode 100644 index 0000000000000000000000000000000000000000..cea9920282a70e191b95faa6bbc3f9fa0d0138a0 --- /dev/null +++ b/src/benchmarks/t_test1/lran2.h @@ -0,0 +1,51 @@ +/* lran2.h + * by Wolfram Gloger 1996. + * + * A small, portable pseudo-random number generator. + */ + +#ifndef _LRAN2_H +#define _LRAN2_H + +#define LRAN2_MAX 714025l /* constants for portable */ +#define IA 1366l /* random number generator */ +#define IC 150889l /* (see e.g. `Numerical Recipes') */ + +struct lran2_st { + long x, y, v[97]; +}; + +static void +lran2_init(struct lran2_st* d, long seed) +{ + long x; + int j; + + x = (IC - seed) % LRAN2_MAX; + if(x < 0) x = -x; + for(j=0; j<97; j++) { + x = (IA*x + IC) % LRAN2_MAX; + d->v[j] = x; + } + d->x = (IA*x + IC) % LRAN2_MAX; + d->y = d->x; +} + +#ifdef __GNUC__ +__inline__ +#endif +static long +lran2(struct lran2_st* d) +{ + int j = (d->y % 97); + + d->y = d->v[j]; + d->x = (IA*d->x + IC) % LRAN2_MAX; + d->v[j] = d->x; + return d->y; +} + +#undef IA +#undef IC + +#endif diff --git a/src/benchmarks/t_test1/t-test.h b/src/benchmarks/t_test1/t-test.h new file mode 100644 index 0000000000000000000000000000000000000000..a52829a41d3efe8b55bf0f286f15b4489115ba8a --- /dev/null +++ b/src/benchmarks/t_test1/t-test.h @@ -0,0 +1,143 @@ +/* + * $Id: t-test.h,v 1.1 2004/11/04 14:32:21 wg Exp $ + * by Wolfram Gloger 1996. + * Common data structures and functions for testing malloc performance. + */ + +/* Testing level */ +#ifndef TEST +#define TEST 0 +#endif + +/* For large allocation sizes, the time required by copying in + realloc() can dwarf all other execution times. Avoid this with a + size threshold. */ +#ifndef REALLOC_MAX +#define REALLOC_MAX 2000 +#endif + +struct bin { + unsigned char *ptr; + unsigned long size; +}; + +#if TEST > 0 + +static void +mem_init(unsigned char *ptr, unsigned long size) +{ + unsigned long i, j; + + if(size == 0) return; + for(i=0; i<size; i+=2047) { + j = (unsigned long)ptr ^ i; + ptr[i] = ((j ^ (j>>8)) & 0xFF); + } + j = (unsigned long)ptr ^ (size-1); + ptr[size-1] = ((j ^ (j>>8)) & 0xFF); +} + +static int +mem_check(unsigned char *ptr, unsigned long size) +{ + unsigned long i, j; + + if(size == 0) return 0; + for(i=0; i<size; i+=2047) { + j = (unsigned long)ptr ^ i; + if(ptr[i] != ((j ^ (j>>8)) & 0xFF)) return 1; + } + j = (unsigned long)ptr ^ (size-1); + if(ptr[size-1] != ((j ^ (j>>8)) & 0xFF)) return 2; + return 0; +} + +static int +zero_check(unsigned* ptr, unsigned long size) +{ + unsigned char* ptr2; + + while(size >= sizeof(*ptr)) { + if(*ptr++ != 0) + return -1; + size -= sizeof(*ptr); + } + ptr2 = (unsigned char*)ptr; + while(size > 0) { + if(*ptr2++ != 0) + return -1; + --size; + } + return 0; +} + +#endif /* TEST > 0 */ + +/* Allocate a bin with malloc(), realloc() or memalign(). r must be a + random number >= 1024. */ + +static void +bin_alloc(struct bin *m, unsigned long size, int r) +{ +#if TEST > 0 + if(mem_check(m->ptr, m->size)) { + printf("memory corrupt!\n"); + exit(1); + } +#endif + r %= 1024; + /*printf("%d ", r);*/ + if(r < 4) { /* memalign */ + if(m->size > 0) free(m->ptr); + m->ptr = (unsigned char *)memalign(sizeof(int) << r, size); + } else if(r < 20) { /* calloc */ + if(m->size > 0) free(m->ptr); + m->ptr = (unsigned char *)calloc(size, 1); +#if TEST > 0 + if(zero_check((unsigned*)m->ptr, size)) { + long i; + for(i=0; i<size; i++) + if(m->ptr[i] != 0) + break; + printf("calloc'ed memory non-zero (ptr=%p, i=%ld)!\n", m->ptr, i); + exit(1); + } +#endif + } else if(r < 100 && m->size < REALLOC_MAX) { /* realloc */ + if(m->size == 0) m->ptr = NULL; + m->ptr = realloc(m->ptr, size); + } else { /* plain malloc */ + if(m->size > 0) free(m->ptr); + m->ptr = (unsigned char *)malloc(size); + } + if(!m->ptr) { + printf("out of memory (r=%d, size=%ld)!\n", r, (long)size); + exit(1); + } + m->size = size; +#if TEST > 0 + mem_init(m->ptr, m->size); +#endif +} + +/* Free a bin. */ + +static void +bin_free(struct bin *m) +{ + if(m->size == 0) return; +#if TEST > 0 + if(mem_check(m->ptr, m->size)) { + printf("memory corrupt!\n"); + exit(1); + } +#endif + free(m->ptr); + m->size = 0; +} + +/* + * Local variables: + * tab-width: 4 + * End: + */ diff --git a/src/benchmarks/t_test1/t-test1.c b/src/benchmarks/t_test1/t-test1.c new file mode 100644 index 0000000000000000000000000000000000000000..86eb034944516818633333649bbfa47178d993b9 --- /dev/null +++ b/src/benchmarks/t_test1/t-test1.c @@ -0,0 +1,401 @@ +/* + * $Id: t-test1.c,v 1.1.1.1 2003/07/02 16:32:09 matthias.urban Exp $ + * by Wolfram Gloger 1996-1999 + * A multi-thread test for malloc performance, maintaining one pool of + * allocated bins per thread. + * + * Fixed condition variable usage, and ported to windows + * Steven Fuerst 2009 + */ + +/* Testing level */ +#ifndef TEST +#define TEST 0 +#endif + +#define N_TOTAL 500 +#ifndef N_THREADS +#define N_THREADS 2 +#endif +#ifndef N_TOTAL_PRINT +#define N_TOTAL_PRINT 50 +#endif +#define STACKSIZE 32768 +#ifndef MEMORY +#define MEMORY (1ULL << 26) +#endif + +#define RANDOM(s) (rng() % (s)) + +#define MSIZE 10000 +#define I_MAX 10000 +#define ACTIONS_MAX 30 + +#include <pthread.h> + +#if (defined __STDC__ && __STDC__) || defined __cplusplus +# include <stdlib.h> +#endif + +#include <stdio.h> + +#ifdef __GCC__ +#include <unistd.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#endif + +#include <sys/types.h> +#include <malloc.h> + + +/* + * Ultra-fast RNG: Use a fast hash of integers. + * 2**64 Period. + * Passes Diehard and TestU01 at maximum settings + */ +static __thread unsigned long long rnd_seed; + +static inline unsigned rng(void) +{ + unsigned long long c = 7319936632422683443ULL; + unsigned long long x = (rnd_seed += c); + + x ^= x >> 32; + x *= c; + x ^= x >> 32; + x *= c; + x ^= x >> 32; + + /* Return lower 32bits */ + return x; +} + +/* For large allocation sizes, the time required by copying in + realloc() can dwarf all other execution times. Avoid this with a + size threshold. */ +#ifndef REALLOC_MAX +#define REALLOC_MAX 2000 +#endif + +struct bin +{ + unsigned char *ptr; + size_t size; +}; + +static pthread_cond_t finish_cond; +static pthread_mutex_t finish_mutex; + +#if TEST > 0 + +static void mem_init(unsigned char *ptr, size_t size) +{ + size_t i, j; + + if (!size) return; + for (i = 0; i < size; i += 2047) + { + j = (size_t)ptr ^ i; + ptr[i] = j ^ (j>>8); + } + j = (size_t)ptr ^ (size - 1); + ptr[size-1] = j ^ (j>>8); +} + +static int mem_check(unsigned char *ptr, size_t size) +{ + size_t i, j; + + if (!size) return 0; + for (i = 0; i < size; i += 2047) + { + j = (size_t)ptr ^ i; + if (ptr[i] != ((j ^ (j>>8)) & 0xFF)) return 1; + } + j = (size_t)ptr ^ (size - 1); + if (ptr[size-1] != ((j ^ (j>>8)) & 0xFF)) return 2; + return 0; +} + +static int zero_check(void *p, size_t size) +{ + unsigned *ptr = p; + unsigned char *ptr2; + + while (size >= sizeof(*ptr)) + { + if (*ptr++) return -1; + size -= sizeof(*ptr); + } + ptr2 = (unsigned char*)ptr; + + while (size > 0) + { + if (*ptr2++) return -1; + --size; + } + return 0; +} + +#endif /* TEST > 0 */ + +/* + * Allocate a bin with malloc(), realloc() or memalign(). + * r must be a random number >= 1024. + */ +static void bin_alloc(struct bin *m, size_t size, unsigned r) +{ +#if TEST > 0 + if (mem_check(m->ptr, m->size)) + { + printf("memory corrupt!\n"); + exit(1); + } +#endif + r %= 1024; + + if (r < 4) + { + /* memalign */ + if (m->size > 0) free(m->ptr); + m->ptr = memalign(sizeof(int) << r, size); + } + else if (r < 20) + { + /* calloc */ + if (m->size > 0) free(m->ptr); + m->ptr = calloc(size, 1); +#if TEST > 0 + if (zero_check(m->ptr, size)) + { + size_t i; + for (i = 0; i < size; i++) + { + if (m->ptr[i]) break; + } + printf("calloc'ed memory non-zero (ptr=%p, i=%ld)!\n", m->ptr, i); + exit(1); + } +#endif + } + else if ((r < 100) && (m->size < REALLOC_MAX)) + { + /* realloc */ + if (!m->size) m->ptr = NULL; + m->ptr = realloc(m->ptr, size); + } + else + { + /* malloc */ + if (m->size > 0) free(m->ptr); + m->ptr = malloc(size); + } + if (!m->ptr) + { + printf("out of memory (r=%d, size=%ld)!\n", r, (unsigned long)size); + exit(1); + } + + m->size = size; +#if TEST > 0 + mem_init(m->ptr, m->size); +#endif +} + +/* Free a bin. */ + +static void bin_free(struct bin *m) +{ + if (!m->size) return; + +#if TEST > 0 + if (mem_check(m->ptr, m->size)) + { + printf("memory corrupt!\n"); + exit(1); + } +#endif + + free(m->ptr); + m->size = 0; +} + +struct bin_info +{ + struct bin *m; + size_t size, bins; +}; + +struct thread_st +{ + int bins, max, flags; + size_t size; + pthread_t id; + char *sp; + size_t seed; +}; + +static void *malloc_test(void *ptr) +{ + struct thread_st *st = ptr; + int i, pid = 1; + unsigned b, j, actions; + struct bin_info p; + + rnd_seed = st->seed; + + p.m = malloc(st->bins * sizeof(*p.m)); + p.bins = st->bins; + p.size = st->size; + for (b = 0; b < p.bins; b++) + { + p.m[b].size = 0; + p.m[b].ptr = NULL; + if (!RANDOM(2)) bin_alloc(&p.m[b], RANDOM(p.size) + 1, rng()); + } + + for (i = 0; i <= st->max;) + { + actions = RANDOM(ACTIONS_MAX); + + for (j = 0; j < actions; j++) + { + b = RANDOM(p.bins); + bin_free(&p.m[b]); + } + i += actions; + actions = RANDOM(ACTIONS_MAX); + + for (j = 0; j < actions; j++) + { + b = RANDOM(p.bins); + bin_alloc(&p.m[b], RANDOM(p.size) + 1, rng()); + } + + i += actions; + } + + for (b = 0; b < p.bins; b++) bin_free(&p.m[b]); + + free(p.m); + + if (pid > 0) + { + pthread_mutex_lock(&finish_mutex); + st->flags = 1; + pthread_cond_signal(&finish_cond); + pthread_mutex_unlock(&finish_mutex); + } + return NULL; +} + +static int my_start_thread(struct thread_st *st) +{ + pthread_create(&st->id, NULL, malloc_test, st); + return 0; +} + +static int n_total = 0; +static int n_total_max = N_TOTAL; +static int n_running; + +static int my_end_thread(struct thread_st *st) +{ + /* Thread st has finished. Start a new one. */ + if (n_total >= n_total_max) + { + n_running--; + } + else if (st->seed++, my_start_thread(st)) + { + printf("Creating thread #%d failed.\n", n_total); + } + else + { + n_total++; + if (!(n_total%N_TOTAL_PRINT)) printf("n_total = %d\n", n_total); + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int i, bins; + int n_thr = N_THREADS; + int i_max = I_MAX; + size_t size = MSIZE; + struct thread_st *st; + + if (argc > 1) n_total_max = atoi(argv[1]); + if (n_total_max < 1) n_thr = 1; + if (argc > 2) n_thr = atoi(argv[2]); + if (n_thr < 1) n_thr = 1; + if (n_thr > 100) n_thr = 100; + if (argc > 3) i_max = atoi(argv[3]); + + if (argc > 4) size = atol(argv[4]); + if (size < 2) size = 2; + + bins = MEMORY /(size * n_thr); + if (argc > 5) bins = atoi(argv[5]); + if (bins < 4) bins = 4; + + printf("Using posix threads.\n"); + pthread_cond_init(&finish_cond, NULL); + pthread_mutex_init(&finish_mutex, NULL); + + printf("total=%d threads=%d i_max=%d size=%ld bins=%d\n", + n_total_max, n_thr, i_max, size, bins); + + st = malloc(n_thr * sizeof(*st)); + if (!st) exit(-1); + + pthread_mutex_lock(&finish_mutex); + + /* Start all n_thr threads. */ + for (i = 0; i < n_thr; i++) + { + st[i].bins = bins; + st[i].max = i_max; + st[i].size = size; + st[i].flags = 0; + st[i].sp = 0; + st[i].seed = (i_max * size + i) ^ bins; + if (my_start_thread(&st[i])) + { + printf("Creating thread #%d failed.\n", i); + n_thr = i; + break; + } + printf("Created thread %lx.\n", (long)st[i].id); + } + + for (n_running = n_total = n_thr; n_running > 0;) + { + + /* Wait for subthreads to finish. */ + pthread_cond_wait(&finish_cond, &finish_mutex); + for (i = 0; i < n_thr; i++) + { + if (st[i].flags) + { + pthread_join(st[i].id, NULL); + st[i].flags = 0; + my_end_thread(&st[i]); + } + } + } + pthread_mutex_unlock(&finish_mutex); + + for (i = 0; i < n_thr; i++) + { + if (st[i].sp) free(st[i].sp); + } + free(st); + malloc_stats(); + printf("Done.\n"); + return 0; +} +