1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2013 Davidlohr Bueso <davidlohr@hp.com>
5 * futex-wake: Block a bunch of threads on a futex and wake'em up, N at a time.
7 * This program is particularly useful to measure the latency of nthread wakeups
8 * in non-error situations: all waiters are queued and all wake calls wakeup
9 * one or more tasks, and thus the waitqueue is never empty.
12 /* For the CLR_() macros */
17 #include "../util/mutex.h"
18 #include "../util/stat.h"
19 #include <subcmd/parse-options.h>
20 #include <linux/compiler.h>
21 #include <linux/kernel.h>
22 #include <linux/time64.h>
24 #include <perf/cpumap.h>
33 /* all threads will block on the same futex */
34 static u_int32_t futex1
= 0;
36 static pthread_t
*worker
;
37 static bool done
= false;
38 static struct mutex thread_lock
;
39 static struct cond thread_parent
, thread_worker
;
40 static struct stats waketime_stats
, wakeup_stats
;
41 static unsigned int threads_starting
;
42 static int futex_flag
= 0;
44 static struct bench_futex_parameters params
= {
46 * How many wakeups to do at a time.
47 * Default to 1 in order to make the kernel work more.
52 static const struct option options
[] = {
53 OPT_UINTEGER('t', "threads", ¶ms
.nthreads
, "Specify amount of threads"),
54 OPT_UINTEGER('w', "nwakes", ¶ms
.nwakes
, "Specify amount of threads to wake at once"),
55 OPT_BOOLEAN( 's', "silent", ¶ms
.silent
, "Silent mode: do not display data/details"),
56 OPT_BOOLEAN( 'S', "shared", ¶ms
.fshared
, "Use shared futexes instead of private ones"),
57 OPT_BOOLEAN( 'm', "mlockall", ¶ms
.mlockall
, "Lock all current and future memory"),
62 static const char * const bench_futex_wake_usage
[] = {
63 "perf bench futex wake <options>",
67 static void *workerfn(void *arg __maybe_unused
)
69 mutex_lock(&thread_lock
);
71 if (!threads_starting
)
72 cond_signal(&thread_parent
);
73 cond_wait(&thread_worker
, &thread_lock
);
74 mutex_unlock(&thread_lock
);
77 if (futex_wait(&futex1
, 0, NULL
, futex_flag
) != EINTR
)
85 static void print_summary(void)
87 double waketime_avg
= avg_stats(&waketime_stats
);
88 double waketime_stddev
= stddev_stats(&waketime_stats
);
89 unsigned int wakeup_avg
= avg_stats(&wakeup_stats
);
91 printf("Wokeup %d of %d threads in %.4f ms (+-%.2f%%)\n",
94 waketime_avg
/ USEC_PER_MSEC
,
95 rel_stddev_stats(waketime_stddev
, waketime_avg
));
98 static void block_threads(pthread_t
*w
, struct perf_cpu_map
*cpu
)
103 int nrcpus
= cpu__max_cpu().cpu
;
104 threads_starting
= params
.nthreads
;
106 cpuset
= CPU_ALLOC(nrcpus
);
108 size
= CPU_ALLOC_SIZE(nrcpus
);
110 /* create and block all threads */
111 for (i
= 0; i
< params
.nthreads
; i
++) {
112 pthread_attr_t thread_attr
;
114 pthread_attr_init(&thread_attr
);
115 CPU_ZERO_S(size
, cpuset
);
116 CPU_SET_S(perf_cpu_map__cpu(cpu
, i
% perf_cpu_map__nr(cpu
)).cpu
, size
, cpuset
);
118 if (pthread_attr_setaffinity_np(&thread_attr
, size
, cpuset
)) {
120 err(EXIT_FAILURE
, "pthread_attr_setaffinity_np");
123 if (pthread_create(&w
[i
], &thread_attr
, workerfn
, NULL
)) {
125 err(EXIT_FAILURE
, "pthread_create");
127 pthread_attr_destroy(&thread_attr
);
132 static void toggle_done(int sig __maybe_unused
,
133 siginfo_t
*info __maybe_unused
,
134 void *uc __maybe_unused
)
139 int bench_futex_wake(int argc
, const char **argv
)
143 struct sigaction act
;
144 struct perf_cpu_map
*cpu
;
146 argc
= parse_options(argc
, argv
, options
, bench_futex_wake_usage
, 0);
148 usage_with_options(bench_futex_wake_usage
, options
);
152 cpu
= perf_cpu_map__new_online_cpus();
154 err(EXIT_FAILURE
, "calloc");
156 memset(&act
, 0, sizeof(act
));
157 sigfillset(&act
.sa_mask
);
158 act
.sa_sigaction
= toggle_done
;
159 sigaction(SIGINT
, &act
, NULL
);
161 if (params
.mlockall
) {
162 if (mlockall(MCL_CURRENT
| MCL_FUTURE
))
163 err(EXIT_FAILURE
, "mlockall");
166 if (!params
.nthreads
)
167 params
.nthreads
= perf_cpu_map__nr(cpu
);
169 worker
= calloc(params
.nthreads
, sizeof(*worker
));
171 err(EXIT_FAILURE
, "calloc");
174 futex_flag
= FUTEX_PRIVATE_FLAG
;
176 printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), "
177 "waking up %d at a time.\n\n",
178 getpid(), params
.nthreads
, params
.fshared
? "shared":"private",
179 &futex1
, params
.nwakes
);
181 init_stats(&wakeup_stats
);
182 init_stats(&waketime_stats
);
183 mutex_init(&thread_lock
);
184 cond_init(&thread_parent
);
185 cond_init(&thread_worker
);
187 for (j
= 0; j
< bench_repeat
&& !done
; j
++) {
188 unsigned int nwoken
= 0;
189 struct timeval start
, end
, runtime
;
191 /* create, launch & block all threads */
192 block_threads(worker
, cpu
);
194 /* make sure all threads are already blocked */
195 mutex_lock(&thread_lock
);
196 while (threads_starting
)
197 cond_wait(&thread_parent
, &thread_lock
);
198 cond_broadcast(&thread_worker
);
199 mutex_unlock(&thread_lock
);
203 /* Ok, all threads are patiently blocked, start waking folks up */
204 gettimeofday(&start
, NULL
);
205 while (nwoken
!= params
.nthreads
)
206 nwoken
+= futex_wake(&futex1
,
207 params
.nwakes
, futex_flag
);
208 gettimeofday(&end
, NULL
);
209 timersub(&end
, &start
, &runtime
);
211 update_stats(&wakeup_stats
, nwoken
);
212 update_stats(&waketime_stats
, runtime
.tv_usec
);
214 if (!params
.silent
) {
215 printf("[Run %d]: Wokeup %d of %d threads in %.4f ms\n",
216 j
+ 1, nwoken
, params
.nthreads
,
217 runtime
.tv_usec
/ (double)USEC_PER_MSEC
);
220 for (i
= 0; i
< params
.nthreads
; i
++) {
221 ret
= pthread_join(worker
[i
], NULL
);
223 err(EXIT_FAILURE
, "pthread_join");
228 /* cleanup & report results */
229 cond_destroy(&thread_parent
);
230 cond_destroy(&thread_worker
);
231 mutex_destroy(&thread_lock
);
236 perf_cpu_map__put(cpu
);