Message ID | 1412349086-11473-2-git-send-email-will.newton@linaro.org |
---|---|
State | Superseded |
Headers | show |
On Fri, Oct 03, 2014 at 04:11:24PM +0100, Will Newton wrote: > Add a microbenchmark for measuring malloc and free performance. The > benchmark allocates and frees buffers of random sizes in a random order > and measures the overall execution time and RSS. > > The random block sizes used follow an inverse square distribution > which is intended to mimic the behaviour of real applications which > tend to allocate many more small blocks than large ones. > > ChangeLog: > > 2014-10-03 Will Newton <will.newton@linaro.org> > > * benchtests/Makefile (stdlib-bench): Add malloc benchmark. > * benchtests/bench-malloc.c: New file. Looks OK to me with one minor nit (pointed out below) fixed. > --- > benchtests/Makefile | 2 +- > benchtests/bench-malloc.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 220 insertions(+), 1 deletion(-) > create mode 100644 benchtests/bench-malloc.c > > diff --git a/benchtests/Makefile b/benchtests/Makefile > index fd3036d..1f8eb82 100644 > --- a/benchtests/Makefile > +++ b/benchtests/Makefile > @@ -37,7 +37,7 @@ string-bench := bcopy bzero memccpy memchr memcmp memcpy memmem memmove \ > strspn strstr strcpy_chk stpcpy_chk memrchr strsep strtok > string-bench-all := $(string-bench) > > -stdlib-bench := strtod > +stdlib-bench := strtod malloc > > benchset := $(string-bench-all) $(stdlib-bench) > > diff --git a/benchtests/bench-malloc.c b/benchtests/bench-malloc.c > new file mode 100644 > index 0000000..54631ed > --- /dev/null > +++ b/benchtests/bench-malloc.c > @@ -0,0 +1,219 @@ > +/* Benchmark malloc and free functions. > + Copyright (C) 2013-2014 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <http://www.gnu.org/licenses/>. */ > + > +#include <errno.h> > +#include <math.h> > +#include <signal.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <sys/time.h> > +#include <sys/resource.h> > +#include <unistd.h> > + > +#include "bench-timing.h" > +#include "json-lib.h" > + > +/* Benchmark duration in seconds. */ > +#define BENCHMARK_DURATION 60 > +#define RAND_SEED 88 > + > +/* Maximum memory that can be allocated at any one time is: > + > + WORKING_SET_SIZE * MAX_ALLOCATION_SIZE > + > + However due to the distribution of the random block sizes > + the typical amount allocated will be much smaller. */ > +#define WORKING_SET_SIZE 1024 > + > +#define MIN_ALLOCATION_SIZE 4 > +#define MAX_ALLOCATION_SIZE 32768 > + > +/* Get a random block size with an inverse square distribution. */ > +static unsigned int > +get_block_size (unsigned int rand_data) > +{ > + /* Inverse square. */ > + const float exponent = -2; > + /* Minimum value of distribution. */ > + const float dist_min = MIN_ALLOCATION_SIZE; > + /* Maximum value of distribution. */ > + const float dist_max = MAX_ALLOCATION_SIZE; > + > + float min_pow = powf (dist_min, exponent + 1); > + float max_pow = powf (dist_max, exponent + 1); > + > + float r = (float) rand_data / RAND_MAX; > + > + return (unsigned int) powf ((max_pow - min_pow) * r + min_pow, 1 / (exponent + 1)); > +} > + > +#define NUM_BLOCK_SIZES 8000 > +#define NUM_OFFSETS ((WORKING_SET_SIZE) * 4) > + > +static unsigned int random_block_sizes[NUM_BLOCK_SIZES]; > +static unsigned int random_offsets[NUM_OFFSETS]; > + > +static void > +init_random_values (void) > +{ > + for (size_t i = 0; i < NUM_BLOCK_SIZES; i++) > + random_block_sizes[i] = get_block_size (rand ()); > + > + for (size_t i = 0; i < NUM_OFFSETS; i++) > + random_offsets[i] = rand () % WORKING_SET_SIZE; > +} > + > +static unsigned int > +get_random_block_size (unsigned int *state) > +{ > + unsigned int idx = *state; > + > + if (idx >= NUM_BLOCK_SIZES - 1) > + idx = 0; > + else > + idx++; > + > + *state = idx; > + > + return random_block_sizes[idx]; > +} > + > +static unsigned int > +get_random_offset (unsigned int *state) > +{ > + unsigned int idx = *state; > + > + if (idx >= NUM_OFFSETS - 1) > + idx = 0; > + else > + idx++; > + > + *state = idx; > + > + return random_offsets[idx]; > +} > + > +static volatile bool timeout; > + > +static void alarm_handler (int signum) Line break after void. > +{ > + timeout = true; > +} > + > +/* Allocate and free blocks in a random order. */ > +static size_t > +malloc_benchmark_loop (void **ptr_arr) > +{ > + unsigned int offset_state = 0, block_state = 0; > + size_t iters = 0; > + > + while (!timeout) > + { > + unsigned int next_idx = get_random_offset (&offset_state); > + unsigned int next_block = get_random_block_size (&block_state); > + > + free (ptr_arr[next_idx]); > + > + ptr_arr[next_idx] = malloc (next_block); > + > + iters++; > + } > + > + return iters; > +} > + > +static timing_t > +do_benchmark (size_t *iters) > +{ > + timing_t elapsed, start, stop; > + void *working_set[WORKING_SET_SIZE]; > + > + memset (working_set, 0, sizeof (working_set)); > + > + TIMING_NOW (start); > + *iters = malloc_benchmark_loop (working_set); > + TIMING_NOW (stop); > + > + TIMING_DIFF (elapsed, start, stop); > + > + return elapsed; > +} > + > +int > +main (int argc, char **argv) > +{ > + timing_t cur; > + size_t iters = 0; > + unsigned long res; > + json_ctx_t json_ctx; > + double d_total_s, d_total_i; > + struct sigaction act; > + > + init_random_values (); > + > + json_init (&json_ctx, 0, stdout); > + > + json_document_begin (&json_ctx); > + > + json_attr_string (&json_ctx, "timing_type", TIMING_TYPE); > + > + json_attr_object_begin (&json_ctx, "functions"); > + > + json_attr_object_begin (&json_ctx, "malloc"); > + > + json_attr_object_begin (&json_ctx, ""); > + > + TIMING_INIT (res); > + > + (void) res; > + > + memset (&act, 0, sizeof (act)); > + act.sa_handler = &alarm_handler; > + > + sigaction (SIGALRM, &act, NULL); > + > + alarm (BENCHMARK_DURATION); > + > + cur = do_benchmark (&iters); > + > + struct rusage usage; > + getrusage(RUSAGE_SELF, &usage); > + > + d_total_s = cur; > + d_total_i = iters; > + > + json_attr_double (&json_ctx, "duration", d_total_s); > + json_attr_double (&json_ctx, "iterations", d_total_i); > + json_attr_double (&json_ctx, "time_per_iteration", d_total_s / d_total_i); > + json_attr_double (&json_ctx, "max_rss", usage.ru_maxrss); > + > + json_attr_double (&json_ctx, "min_size", MIN_ALLOCATION_SIZE); > + json_attr_double (&json_ctx, "max_size", MAX_ALLOCATION_SIZE); > + json_attr_double (&json_ctx, "random_seed", RAND_SEED); > + > + json_attr_object_end (&json_ctx); > + > + json_attr_object_end (&json_ctx); > + > + json_attr_object_end (&json_ctx); > + > + json_document_end (&json_ctx); > + > + return 0; > +} > -- > 1.9.3 >
diff --git a/benchtests/Makefile b/benchtests/Makefile index fd3036d..1f8eb82 100644 --- a/benchtests/Makefile +++ b/benchtests/Makefile @@ -37,7 +37,7 @@ string-bench := bcopy bzero memccpy memchr memcmp memcpy memmem memmove \ strspn strstr strcpy_chk stpcpy_chk memrchr strsep strtok string-bench-all := $(string-bench) -stdlib-bench := strtod +stdlib-bench := strtod malloc benchset := $(string-bench-all) $(stdlib-bench) diff --git a/benchtests/bench-malloc.c b/benchtests/bench-malloc.c new file mode 100644 index 0000000..54631ed --- /dev/null +++ b/benchtests/bench-malloc.c @@ -0,0 +1,219 @@ +/* Benchmark malloc and free functions. + Copyright (C) 2013-2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> + +#include "bench-timing.h" +#include "json-lib.h" + +/* Benchmark duration in seconds. */ +#define BENCHMARK_DURATION 60 +#define RAND_SEED 88 + +/* Maximum memory that can be allocated at any one time is: + + WORKING_SET_SIZE * MAX_ALLOCATION_SIZE + + However due to the distribution of the random block sizes + the typical amount allocated will be much smaller. */ +#define WORKING_SET_SIZE 1024 + +#define MIN_ALLOCATION_SIZE 4 +#define MAX_ALLOCATION_SIZE 32768 + +/* Get a random block size with an inverse square distribution. */ +static unsigned int +get_block_size (unsigned int rand_data) +{ + /* Inverse square. */ + const float exponent = -2; + /* Minimum value of distribution. */ + const float dist_min = MIN_ALLOCATION_SIZE; + /* Maximum value of distribution. */ + const float dist_max = MAX_ALLOCATION_SIZE; + + float min_pow = powf (dist_min, exponent + 1); + float max_pow = powf (dist_max, exponent + 1); + + float r = (float) rand_data / RAND_MAX; + + return (unsigned int) powf ((max_pow - min_pow) * r + min_pow, 1 / (exponent + 1)); +} + +#define NUM_BLOCK_SIZES 8000 +#define NUM_OFFSETS ((WORKING_SET_SIZE) * 4) + +static unsigned int random_block_sizes[NUM_BLOCK_SIZES]; +static unsigned int random_offsets[NUM_OFFSETS]; + +static void +init_random_values (void) +{ + for (size_t i = 0; i < NUM_BLOCK_SIZES; i++) + random_block_sizes[i] = get_block_size (rand ()); + + for (size_t i = 0; i < NUM_OFFSETS; i++) + random_offsets[i] = rand () % WORKING_SET_SIZE; +} + +static unsigned int +get_random_block_size (unsigned int *state) +{ + unsigned int idx = *state; + + if (idx >= NUM_BLOCK_SIZES - 1) + idx = 0; + else + idx++; + + *state = idx; + + return random_block_sizes[idx]; +} + +static unsigned int +get_random_offset (unsigned int *state) +{ + unsigned int idx = *state; + + if (idx >= NUM_OFFSETS - 1) + idx = 0; + else + idx++; + + *state = idx; + + return random_offsets[idx]; +} + +static volatile bool timeout; + +static void alarm_handler (int signum) +{ + timeout = true; +} + +/* Allocate and free blocks in a random order. */ +static size_t +malloc_benchmark_loop (void **ptr_arr) +{ + unsigned int offset_state = 0, block_state = 0; + size_t iters = 0; + + while (!timeout) + { + unsigned int next_idx = get_random_offset (&offset_state); + unsigned int next_block = get_random_block_size (&block_state); + + free (ptr_arr[next_idx]); + + ptr_arr[next_idx] = malloc (next_block); + + iters++; + } + + return iters; +} + +static timing_t +do_benchmark (size_t *iters) +{ + timing_t elapsed, start, stop; + void *working_set[WORKING_SET_SIZE]; + + memset (working_set, 0, sizeof (working_set)); + + TIMING_NOW (start); + *iters = malloc_benchmark_loop (working_set); + TIMING_NOW (stop); + + TIMING_DIFF (elapsed, start, stop); + + return elapsed; +} + +int +main (int argc, char **argv) +{ + timing_t cur; + size_t iters = 0; + unsigned long res; + json_ctx_t json_ctx; + double d_total_s, d_total_i; + struct sigaction act; + + init_random_values (); + + json_init (&json_ctx, 0, stdout); + + json_document_begin (&json_ctx); + + json_attr_string (&json_ctx, "timing_type", TIMING_TYPE); + + json_attr_object_begin (&json_ctx, "functions"); + + json_attr_object_begin (&json_ctx, "malloc"); + + json_attr_object_begin (&json_ctx, ""); + + TIMING_INIT (res); + + (void) res; + + memset (&act, 0, sizeof (act)); + act.sa_handler = &alarm_handler; + + sigaction (SIGALRM, &act, NULL); + + alarm (BENCHMARK_DURATION); + + cur = do_benchmark (&iters); + + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); + + d_total_s = cur; + d_total_i = iters; + + json_attr_double (&json_ctx, "duration", d_total_s); + json_attr_double (&json_ctx, "iterations", d_total_i); + json_attr_double (&json_ctx, "time_per_iteration", d_total_s / d_total_i); + json_attr_double (&json_ctx, "max_rss", usage.ru_maxrss); + + json_attr_double (&json_ctx, "min_size", MIN_ALLOCATION_SIZE); + json_attr_double (&json_ctx, "max_size", MAX_ALLOCATION_SIZE); + json_attr_double (&json_ctx, "random_seed", RAND_SEED); + + json_attr_object_end (&json_ctx); + + json_attr_object_end (&json_ctx); + + json_attr_object_end (&json_ctx); + + json_document_end (&json_ctx); + + return 0; +}