/*- * Copyright (C) 2008 Jason Evans . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice(s), this list of conditions and the following disclaimer as * the first lines of this file unmodified other than the possible * addition of one or more copyright notices. * 2. Redistributions in binary form must reproduce the above copyright * notice(s), this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************* * * This microbenchmark tests concurrent throughput of malloc(3), but each * thread performs a unique number of allocation/deallocation cycles in order * potentially create a dynamic imbalance in the number of active threads as * threads finish varying amounts of work. * ******************************************************************************* */ #include #include #include #include #include #include #include #include #include typedef struct { unsigned long thread; bool verbose; unsigned long long allocs; size_t minsize; size_t maxsize; } thread_args_t; void * thread_enter(void *vargs) { thread_args_t *args = (thread_args_t *)vargs; unsigned long long i, size_total; unsigned ctx; size_t size; void *ptr; for (i = size_total = 0, ctx = (unsigned)args->thread; i < args->allocs; i++, size_total += size) { size = args->minsize + (((float)rand_r(&ctx) / (float)RAND_MAX) * (args->maxsize - args->minsize)); ptr = calloc(1, size); if (ptr == NULL) { fprintf(stderr, "imbalance: Thread %lu: Error in calloc(1, %zu)\n", args->thread, size); _exit(1); } free(ptr); } if (args->verbose) { fprintf(stderr, "Thread %lu completed %llu cycles, %llu bytes\n", args->thread, args->allocs, size_total); } return (NULL); } void usage(FILE *fp) { fprintf(fp, "imbalance usage:\n"); fprintf(fp, " imbalance []\n"); fprintf(fp, "\n"); fprintf(fp, " Option | Description\n"); fprintf(fp, " -------------+-----------------------------\n"); fprintf(fp, " -h | Print usage and exit.\n"); fprintf(fp, " -v | Print verbose output.\n"); fprintf(fp, " -t | Number of worker threads.\n"); fprintf(fp, " -x | Allocation count multiplier.\n"); fprintf(fp, " -s | Minimum allocation size.\n"); fprintf(fp, " -S | Maximum allocation size.\n"); } int main(int argc, char **argv) { int c, err; bool opt_verbose = false; unsigned long opt_threads = 16; unsigned long long opt_mult = (1LLU << 28); size_t opt_minsize = 1; size_t opt_maxsize = 8192; pthread_t *threads; thread_args_t *thread_args; unsigned long i; opterr = 0; optind = 1; while ((c = getopt(argc, argv, "hvt:x:s:S:")) != -1) { switch (c) { case 'h': usage(stdout); _exit(0); case 'v': opt_verbose = true; break; case 't': errno = 0; opt_threads = strtoul(optarg, NULL, 0); if (opt_threads == ULONG_MAX && errno != 0) { fprintf(stderr, "imbalance: Error in option '-%c %s': %s\n", c, optarg, strerror(errno)); usage(stderr); _exit(1); } else if (opt_threads == 0) { fprintf(stderr, "imbalance: Invalid option '-%c %s'\n", c, optarg); usage(stderr); _exit(1); } break; case 'x': errno = 0; opt_mult = strtoull(optarg, NULL, 0); if (opt_mult == ULLONG_MAX && errno != 0) { fprintf(stderr, "imbalance: Error in option '-%c %s': %s\n", c, optarg, strerror(errno)); usage(stderr); _exit(1); } else if (opt_mult == 0) { fprintf(stderr, "imbalance: Invalid option '-%c %s'\n", c, optarg); usage(stderr); _exit(1); } break; case 's': errno = 0; opt_minsize = strtoull(optarg, NULL, 0); if (opt_minsize == ULLONG_MAX && errno != 0) { fprintf(stderr, "imbalance: Error in option '-%c %s': %s\n", c, optarg, strerror(errno)); usage(stderr); _exit(1); } break; case 'S': errno = 0; opt_maxsize = strtoull(optarg, NULL, 0); if (opt_maxsize == ULLONG_MAX && errno != 0) { fprintf(stderr, "imbalance: Error in option '-%c %s': %s\n", c, optarg, strerror(errno)); usage(stderr); _exit(1); } break; default: fprintf(stderr, "imbalance: Unrecognized option\n"); usage(stderr); _exit(1); } } if (opt_maxsize < opt_minsize) { fprintf(stderr, "imbalance: Maximum size smaller than minimum size" " (%zu < %zu)\n", opt_maxsize, opt_minsize); usage(stderr); _exit(1); } if (opt_verbose) { fprintf(stdout, "imbalance: Verbose output enabled\n"); fprintf(stdout, "imbalance: Number of threads: %lu\n", opt_threads); fprintf(stdout, "imbalance: Allocation count multiplier: %llu\n", opt_mult); fprintf(stdout, "imbalance: Allocation size [%zu..%zu]\n", opt_minsize, opt_maxsize); } threads = (pthread_t *)malloc(sizeof(pthread_t) * opt_threads); if (threads == NULL) { fprintf(stderr, "imbalance: Error in malloc()\n"); _exit(1); } thread_args = (thread_args_t *)malloc(sizeof(thread_args_t) * opt_threads); if (thread_args == NULL) { fprintf(stderr, "imbalance: Error in malloc()\n"); _exit(1); } for (i = 0; i < opt_threads; i++) { thread_args[i].thread = i; thread_args[i].verbose = opt_verbose; thread_args[i].allocs = opt_mult * (unsigned long long)(i + 1); thread_args[i].minsize = opt_minsize; thread_args[i].maxsize = opt_maxsize; } /* Permute the allocs fields within the thread_args array. */ unsigned ctx; for (i = 0, ctx = (unsigned)getpid(); i < opt_threads; i++) { unsigned long long t; unsigned long pick = i + (((float)rand_r(&ctx) / (float)RAND_MAX) * (opt_threads - i - 1)); t = thread_args[i].allocs; thread_args[i].allocs = thread_args[pick].allocs; thread_args[pick].allocs = t; } for (i = 0; i < opt_threads; i++) { if ((err = pthread_create(&threads[i], NULL, thread_enter, (void *)&thread_args[i])) != 0) { fprintf(stderr, "imbalance: Error in pthread_create(): %s\n", strerror(err)); _exit(1); } } for (i = 0; i < opt_threads; i++) { if ((err = pthread_join(threads[i], NULL)) != 0) { fprintf(stderr, "imbalance: Error in pthread_join(): %s\n", strerror(err)); _exit(1); } } return (0); }