11 #include <linux/compiler.h> 12 #include "../util/debug.h" 14 #ifndef HAVE_PTHREAD_BARRIER 17 pr_err(
"%s: pthread_barrier_t unavailable, disabling this test...\n", __func__);
26 #include "../util/stat.h" 27 #include <subcmd/parse-options.h> 28 #include <linux/kernel.h> 29 #include <linux/time64.h> 41 struct timeval runtime;
44 static unsigned int nwakes = 1;
47 static u_int32_t
futex = 0;
49 static pthread_t *blocked_worker;
51 static unsigned int nblocked_threads = 0, nwaking_threads = 0;
54 static pthread_barrier_t barrier;
55 static struct stats waketime_stats, wakeup_stats;
59 static const struct option
options[] = {
60 OPT_UINTEGER(
't',
"threads", &nblocked_threads,
"Specify amount of threads"),
61 OPT_UINTEGER(
'w',
"nwakers", &nwaking_threads,
"Specify amount of waking threads"),
62 OPT_BOOLEAN(
's',
"silent", &
silent,
"Silent mode: do not display data/details"),
63 OPT_BOOLEAN(
'S',
"shared", &
fshared,
"Use shared futexes instead of private ones"),
67 static const char *
const bench_futex_wake_parallel_usage[] = {
68 "perf bench futex wake-parallel <options>",
72 static void *waking_workerfn(
void *arg)
75 struct timeval start, end;
77 pthread_barrier_wait(&barrier);
79 gettimeofday(&start, NULL);
82 if (waker->nwoken !=
nwakes)
83 warnx(
"couldn't wakeup all tasks (%d/%d)",
86 gettimeofday(&end, NULL);
87 timersub(&end, &start, &waker->runtime);
93 static void wakeup_threads(
struct thread_data *td, pthread_attr_t thread_attr)
97 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
99 pthread_barrier_init(&barrier, NULL, nwaking_threads + 1);
102 for (i = 0; i < nwaking_threads; i++) {
108 if (pthread_create(&td[i].
worker, &thread_attr,
109 waking_workerfn, (
void *)&td[i]))
110 err(EXIT_FAILURE,
"pthread_create");
113 pthread_barrier_wait(&barrier);
115 for (i = 0; i < nwaking_threads; i++)
116 if (pthread_join(td[i].
worker, NULL))
117 err(EXIT_FAILURE,
"pthread_join");
119 pthread_barrier_destroy(&barrier);
122 static void *blocked_workerfn(
void *arg __maybe_unused)
140 static void block_threads(pthread_t *w, pthread_attr_t thread_attr,
149 for (i = 0; i < nblocked_threads; i++) {
151 CPU_SET(cpu->
map[i % cpu->
nr], &cpuset);
154 err(EXIT_FAILURE,
"pthread_attr_setaffinity_np");
156 if (pthread_create(&w[i], &thread_attr, blocked_workerfn, NULL))
157 err(EXIT_FAILURE,
"pthread_create");
161 static void print_run(
struct thread_data *waking_worker,
unsigned int run_num)
163 unsigned int i, wakeup_avg;
164 double waketime_avg, waketime_stddev;
165 struct stats __waketime_stats, __wakeup_stats;
170 for (i = 0; i < nwaking_threads; i++) {
175 waketime_avg =
avg_stats(&__waketime_stats);
179 printf(
"[Run %d]: Avg per-thread latency (waking %d/%d threads) " 180 "in %.4f ms (+-%.2f%%)\n", run_num + 1, wakeup_avg,
181 nblocked_threads, waketime_avg / USEC_PER_MSEC,
187 unsigned int wakeup_avg;
188 double waketime_avg, waketime_stddev;
190 waketime_avg =
avg_stats(&waketime_stats);
194 printf(
"Avg per-thread latency (waking %d/%d threads) in %.4f ms (+-%.2f%%)\n",
197 waketime_avg / USEC_PER_MSEC,
202 static void do_run_stats(
struct thread_data *waking_worker)
206 for (i = 0; i < nwaking_threads; i++) {
214 siginfo_t *info __maybe_unused,
215 void *uc __maybe_unused)
224 struct sigaction act;
225 pthread_attr_t thread_attr;
230 bench_futex_wake_parallel_usage, 0);
232 usage_with_options(bench_futex_wake_parallel_usage,
options);
236 sigfillset(&act.sa_mask);
238 sigaction(SIGINT, &act, NULL);
242 err(EXIT_FAILURE,
"calloc");
244 if (!nblocked_threads)
245 nblocked_threads = cpu->
nr;
248 if (nwaking_threads > nblocked_threads || !nwaking_threads)
249 nwaking_threads = nblocked_threads;
251 if (nblocked_threads % nwaking_threads)
252 errx(EXIT_FAILURE,
"Must be perfectly divisible");
257 nwakes = nblocked_threads/nwaking_threads;
259 blocked_worker = calloc(nblocked_threads,
sizeof(*blocked_worker));
261 err(EXIT_FAILURE,
"calloc");
266 printf(
"Run summary [PID %d]: blocking on %d threads (at [%s] " 267 "futex %p), %d threads waking up %d at a time.\n\n",
268 getpid(), nblocked_threads,
fshared ?
"shared":
"private",
274 pthread_attr_init(&thread_attr);
280 waking_worker = calloc(nwaking_threads,
sizeof(*waking_worker));
282 err(EXIT_FAILURE,
"calloc");
297 wakeup_threads(waking_worker, thread_attr);
299 for (i = 0; i < nblocked_threads; i++) {
300 ret = pthread_join(blocked_worker[i], NULL);
302 err(EXIT_FAILURE,
"pthread_join");
305 do_run_stats(waking_worker);
307 print_run(waking_worker, j);
316 pthread_attr_destroy(&thread_attr);
320 free(blocked_worker);
double avg_stats(struct stats *stats)
int bench_futex_wake_parallel(int argc __maybe_unused, const char **argv __maybe_unused)
static pthread_mutex_t thread_lock
double stddev_stats(struct stats *stats)
#define futex(uaddr, op, val, timeout, uaddr2, val3, opflags)
static void init_stats(struct stats *stats)
void update_stats(struct stats *stats, u64 val)
static int pthread_attr_setaffinity_np(pthread_attr_t *attr __maybe_unused, size_t cpusetsize __maybe_unused, cpu_set_t *cpuset __maybe_unused)
double rel_stddev_stats(double stddev, double avg)
static void block_threads(pthread_t *w, pthread_attr_t thread_attr, struct cpu_map *cpu)
static pthread_cond_t thread_parent
static unsigned int nwakes
static void print_summary(void)
static void toggle_done(int sig __maybe_unused, siginfo_t *info __maybe_unused, void *uc __maybe_unused)
static int futex_wait(u_int32_t *uaddr, u_int32_t val, struct timespec *timeout, int opflags)
static unsigned int threads_starting
struct cpu_map * cpu_map__new(const char *cpu_list)
unsigned int bench_repeat
static int futex_wake(u_int32_t *uaddr, int nr_wake, int opflags)
static pthread_cond_t thread_worker
struct timeval start end runtime