root/tools/perf/bench/futex-hash.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. workerfn
  2. toggle_done
  3. print_summary
  4. bench_futex_hash

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

/* [<][>][^][v][top][bottom][index][help] */