linux/tools/perf/bench/futex-hash.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2013  Davidlohr Bueso <davidlohr@hp.com>
   4 *
   5 * futex-hash: Stress the hell out of the Linux kernel futex uaddr hashing.
   6 *
   7 * This program is particularly useful for measuring the kernel's futex hash
   8 * table/function implementation. In order for it to make sense, use with as
   9 * many threads and futexes as possible.
  10 */
  11
  12/* For the CLR_() macros */
  13#include <string.h>
  14#include <pthread.h>
  15
  16#include <errno.h>
  17#include <signal.h>
  18#include <stdlib.h>
  19#include <linux/compiler.h>
  20#include <linux/kernel.h>
  21#include <sys/time.h>
  22
  23#include "../util/stat.h"
  24#include <subcmd/parse-options.h>
  25#include "bench.h"
  26#include "futex.h"
  27#include "cpumap.h"
  28
  29#include <err.h>
  30
  31static unsigned int nthreads = 0;
  32static unsigned int nsecs    = 10;
  33/* amount of futexes per thread */
  34static unsigned int nfutexes = 1024;
  35static bool fshared = false, done = false, silent = false;
  36static int futex_flag = 0;
  37
  38struct timeval start, end, runtime;
  39static pthread_mutex_t thread_lock;
  40static unsigned int threads_starting;
  41static struct stats throughput_stats;
  42static pthread_cond_t thread_parent, thread_worker;
  43
  44struct worker {
  45        int tid;
  46        u_int32_t *futex;
  47        pthread_t thread;
  48        unsigned long ops;
  49};
  50
  51static const struct option options[] = {
  52        OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
  53        OPT_UINTEGER('r', "runtime", &nsecs,    "Specify runtime (in seconds)"),
  54        OPT_UINTEGER('f', "futexes", &nfutexes, "Specify amount of futexes per threads"),
  55        OPT_BOOLEAN( 's', "silent",  &silent,   "Silent mode: do not display data/details"),
  56        OPT_BOOLEAN( 'S', "shared",  &fshared,  "Use shared futexes instead of private ones"),
  57        OPT_END()
  58};
  59
  60static const char * const bench_futex_hash_usage[] = {
  61        "perf bench futex hash <options>",
  62        NULL
  63};
  64
  65static void *workerfn(void *arg)
  66{
  67        int ret;
  68        struct worker *w = (struct worker *) arg;
  69        unsigned int i;
  70        unsigned long ops = w->ops; /* avoid cacheline bouncing */
  71
  72        pthread_mutex_lock(&thread_lock);
  73        threads_starting--;
  74        if (!threads_starting)
  75                pthread_cond_signal(&thread_parent);
  76        pthread_cond_wait(&thread_worker, &thread_lock);
  77        pthread_mutex_unlock(&thread_lock);
  78
  79        do {
  80                for (i = 0; i < nfutexes; i++, ops++) {
  81                        /*
  82                         * We want the futex calls to fail in order to stress
  83                         * the hashing of uaddr and not measure other steps,
  84                         * such as internal waitqueue handling, thus enlarging
  85                         * the critical region protected by hb->lock.
  86                         */
  87                        ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag);
  88                        if (!silent &&
  89                            (!ret || errno != EAGAIN || errno != EWOULDBLOCK))
  90                                warn("Non-expected futex return call");
  91                }
  92        }  while (!done);
  93
  94        w->ops = ops;
  95        return NULL;
  96}
  97
  98static void toggle_done(int sig __maybe_unused,
  99                        siginfo_t *info __maybe_unused,
 100                        void *uc __maybe_unused)
 101{
 102        /* inform all threads that we're done for the day */
 103        done = true;
 104        gettimeofday(&end, NULL);
 105        timersub(&end, &start, &runtime);
 106}
 107
 108static void print_summary(void)
 109{
 110        unsigned long avg = avg_stats(&throughput_stats);
 111        double stddev = stddev_stats(&throughput_stats);
 112
 113        printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
 114               !silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
 115               (int) runtime.tv_sec);
 116}
 117
 118int bench_futex_hash(int argc, const char **argv)
 119{
 120        int ret = 0;
 121        cpu_set_t cpuset;
 122        struct sigaction act;
 123        unsigned int i;
 124        pthread_attr_t thread_attr;
 125        struct worker *worker = NULL;
 126        struct cpu_map *cpu;
 127
 128        argc = parse_options(argc, argv, options, bench_futex_hash_usage, 0);
 129        if (argc) {
 130                usage_with_options(bench_futex_hash_usage, options);
 131                exit(EXIT_FAILURE);
 132        }
 133
 134        cpu = cpu_map__new(NULL);
 135        if (!cpu)
 136                goto errmem;
 137
 138        sigfillset(&act.sa_mask);
 139        act.sa_sigaction = toggle_done;
 140        sigaction(SIGINT, &act, NULL);
 141
 142        if (!nthreads) /* default to the number of CPUs */
 143                nthreads = cpu->nr;
 144
 145        worker = calloc(nthreads, sizeof(*worker));
 146        if (!worker)
 147                goto errmem;
 148
 149        if (!fshared)
 150                futex_flag = FUTEX_PRIVATE_FLAG;
 151
 152        printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n",
 153               getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs);
 154
 155        init_stats(&throughput_stats);
 156        pthread_mutex_init(&thread_lock, NULL);
 157        pthread_cond_init(&thread_parent, NULL);
 158        pthread_cond_init(&thread_worker, NULL);
 159
 160        threads_starting = nthreads;
 161        pthread_attr_init(&thread_attr);
 162        gettimeofday(&start, NULL);
 163        for (i = 0; i < nthreads; i++) {
 164                worker[i].tid = i;
 165                worker[i].futex = calloc(nfutexes, sizeof(*worker[i].futex));
 166                if (!worker[i].futex)
 167                        goto errmem;
 168
 169                CPU_ZERO(&cpuset);
 170                CPU_SET(cpu->map[i % cpu->nr], &cpuset);
 171
 172                ret = pthread_attr_setaffinity_np(&thread_attr, sizeof(cpu_set_t), &cpuset);
 173                if (ret)
 174                        err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
 175
 176                ret = pthread_create(&worker[i].thread, &thread_attr, workerfn,
 177                                     (void *)(struct worker *) &worker[i]);
 178                if (ret)
 179                        err(EXIT_FAILURE, "pthread_create");
 180
 181        }
 182        pthread_attr_destroy(&thread_attr);
 183
 184        pthread_mutex_lock(&thread_lock);
 185        while (threads_starting)
 186                pthread_cond_wait(&thread_parent, &thread_lock);
 187        pthread_cond_broadcast(&thread_worker);
 188        pthread_mutex_unlock(&thread_lock);
 189
 190        sleep(nsecs);
 191        toggle_done(0, NULL, NULL);
 192
 193        for (i = 0; i < nthreads; i++) {
 194                ret = pthread_join(worker[i].thread, NULL);
 195                if (ret)
 196                        err(EXIT_FAILURE, "pthread_join");
 197        }
 198
 199        /* cleanup & report results */
 200        pthread_cond_destroy(&thread_parent);
 201        pthread_cond_destroy(&thread_worker);
 202        pthread_mutex_destroy(&thread_lock);
 203
 204        for (i = 0; i < nthreads; i++) {
 205                unsigned long t = worker[i].ops/runtime.tv_sec;
 206                update_stats(&throughput_stats, t);
 207                if (!silent) {
 208                        if (nfutexes == 1)
 209                                printf("[thread %2d] futex: %p [ %ld ops/sec ]\n",
 210                                       worker[i].tid, &worker[i].futex[0], t);
 211                        else
 212                                printf("[thread %2d] futexes: %p ... %p [ %ld ops/sec ]\n",
 213                                       worker[i].tid, &worker[i].futex[0],
 214                                       &worker[i].futex[nfutexes-1], t);
 215                }
 216
 217                free(worker[i].futex);
 218        }
 219
 220        print_summary();
 221
 222        free(worker);
 223        free(cpu);
 224        return ret;
 225errmem:
 226        err(EXIT_FAILURE, "calloc");
 227}
 228