1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020 Facebook */
5 #include <linux/compiler.h>
10 #include <sys/sysinfo.h>
14 #include "testing_helpers.h"
25 static int libbpf_print_fn(enum libbpf_print_level level
,
26 const char *format
, va_list args
)
28 if (level
== LIBBPF_DEBUG
&& !env
.verbose
)
30 return vfprintf(stderr
, format
, args
);
33 void setup_libbpf(void)
35 libbpf_set_strict_mode(LIBBPF_STRICT_ALL
);
36 libbpf_set_print(libbpf_print_fn
);
39 void false_hits_report_progress(int iter
, struct bench_res
*res
, long delta_ns
)
41 long total
= res
->false_hits
+ res
->hits
+ res
->drops
;
43 printf("Iter %3d (%7.3lfus): ",
44 iter
, (delta_ns
- 1000000000) / 1000.0);
46 printf("%ld false hits of %ld total operations. Percentage = %2.2f %%\n",
47 res
->false_hits
, total
, ((float)res
->false_hits
/ total
) * 100);
50 void false_hits_report_final(struct bench_res res
[], int res_cnt
)
52 long total_hits
= 0, total_drops
= 0, total_false_hits
= 0, total_ops
= 0;
55 for (i
= 0; i
< res_cnt
; i
++) {
56 total_hits
+= res
[i
].hits
;
57 total_false_hits
+= res
[i
].false_hits
;
58 total_drops
+= res
[i
].drops
;
60 total_ops
= total_hits
+ total_false_hits
+ total_drops
;
62 printf("Summary: %ld false hits of %ld total operations. ",
63 total_false_hits
, total_ops
);
64 printf("Percentage = %2.2f %%\n",
65 ((float)total_false_hits
/ total_ops
) * 100);
68 void hits_drops_report_progress(int iter
, struct bench_res
*res
, long delta_ns
)
70 double hits_per_sec
, drops_per_sec
;
73 hits_per_sec
= res
->hits
/ 1000000.0 / (delta_ns
/ 1000000000.0);
74 hits_per_prod
= hits_per_sec
/ env
.producer_cnt
;
75 drops_per_sec
= res
->drops
/ 1000000.0 / (delta_ns
/ 1000000000.0);
77 printf("Iter %3d (%7.3lfus): ",
78 iter
, (delta_ns
- 1000000000) / 1000.0);
80 printf("hits %8.3lfM/s (%7.3lfM/prod), drops %8.3lfM/s, total operations %8.3lfM/s\n",
81 hits_per_sec
, hits_per_prod
, drops_per_sec
, hits_per_sec
+ drops_per_sec
);
85 grace_period_latency_basic_stats(struct bench_res res
[], int res_cnt
, struct basic_stats
*gp_stat
)
89 memset(gp_stat
, 0, sizeof(struct basic_stats
));
91 for (i
= 0; i
< res_cnt
; i
++)
92 gp_stat
->mean
+= res
[i
].gp_ns
/ 1000.0 / (double)res
[i
].gp_ct
/ (0.0 + res_cnt
);
94 #define IT_MEAN_DIFF (res[i].gp_ns / 1000.0 / (double)res[i].gp_ct - gp_stat->mean)
96 for (i
= 0; i
< res_cnt
; i
++)
97 gp_stat
->stddev
+= (IT_MEAN_DIFF
* IT_MEAN_DIFF
) / (res_cnt
- 1.0);
99 gp_stat
->stddev
= sqrt(gp_stat
->stddev
);
104 grace_period_ticks_basic_stats(struct bench_res res
[], int res_cnt
, struct basic_stats
*gp_stat
)
108 memset(gp_stat
, 0, sizeof(struct basic_stats
));
109 for (i
= 0; i
< res_cnt
; i
++)
110 gp_stat
->mean
+= res
[i
].stime
/ (double)res
[i
].gp_ct
/ (0.0 + res_cnt
);
112 #define IT_MEAN_DIFF (res[i].stime / (double)res[i].gp_ct - gp_stat->mean)
114 for (i
= 0; i
< res_cnt
; i
++)
115 gp_stat
->stddev
+= (IT_MEAN_DIFF
* IT_MEAN_DIFF
) / (res_cnt
- 1.0);
117 gp_stat
->stddev
= sqrt(gp_stat
->stddev
);
121 void hits_drops_report_final(struct bench_res res
[], int res_cnt
)
124 double hits_mean
= 0.0, drops_mean
= 0.0, total_ops_mean
= 0.0;
125 double hits_stddev
= 0.0, drops_stddev
= 0.0, total_ops_stddev
= 0.0;
128 for (i
= 0; i
< res_cnt
; i
++) {
129 hits_mean
+= res
[i
].hits
/ 1000000.0 / (0.0 + res_cnt
);
130 drops_mean
+= res
[i
].drops
/ 1000000.0 / (0.0 + res_cnt
);
132 total_ops_mean
= hits_mean
+ drops_mean
;
135 for (i
= 0; i
< res_cnt
; i
++) {
136 hits_stddev
+= (hits_mean
- res
[i
].hits
/ 1000000.0) *
137 (hits_mean
- res
[i
].hits
/ 1000000.0) /
139 drops_stddev
+= (drops_mean
- res
[i
].drops
/ 1000000.0) *
140 (drops_mean
- res
[i
].drops
/ 1000000.0) /
142 total_ops
= res
[i
].hits
+ res
[i
].drops
;
143 total_ops_stddev
+= (total_ops_mean
- total_ops
/ 1000000.0) *
144 (total_ops_mean
- total_ops
/ 1000000.0) /
147 hits_stddev
= sqrt(hits_stddev
);
148 drops_stddev
= sqrt(drops_stddev
);
149 total_ops_stddev
= sqrt(total_ops_stddev
);
151 printf("Summary: hits %8.3lf \u00B1 %5.3lfM/s (%7.3lfM/prod), ",
152 hits_mean
, hits_stddev
, hits_mean
/ env
.producer_cnt
);
153 printf("drops %8.3lf \u00B1 %5.3lfM/s, ",
154 drops_mean
, drops_stddev
);
155 printf("total operations %8.3lf \u00B1 %5.3lfM/s\n",
156 total_ops_mean
, total_ops_stddev
);
159 void ops_report_progress(int iter
, struct bench_res
*res
, long delta_ns
)
161 double hits_per_sec
, hits_per_prod
;
163 hits_per_sec
= res
->hits
/ 1000000.0 / (delta_ns
/ 1000000000.0);
164 hits_per_prod
= hits_per_sec
/ env
.producer_cnt
;
166 printf("Iter %3d (%7.3lfus): ", iter
, (delta_ns
- 1000000000) / 1000.0);
168 printf("hits %8.3lfM/s (%7.3lfM/prod)\n", hits_per_sec
, hits_per_prod
);
171 void ops_report_final(struct bench_res res
[], int res_cnt
)
173 double hits_mean
= 0.0, hits_stddev
= 0.0;
176 for (i
= 0; i
< res_cnt
; i
++)
177 hits_mean
+= res
[i
].hits
/ 1000000.0 / (0.0 + res_cnt
);
180 for (i
= 0; i
< res_cnt
; i
++)
181 hits_stddev
+= (hits_mean
- res
[i
].hits
/ 1000000.0) *
182 (hits_mean
- res
[i
].hits
/ 1000000.0) /
185 hits_stddev
= sqrt(hits_stddev
);
187 printf("Summary: throughput %8.3lf \u00B1 %5.3lf M ops/s (%7.3lfM ops/prod), ",
188 hits_mean
, hits_stddev
, hits_mean
/ env
.producer_cnt
);
189 printf("latency %8.3lf ns/op\n", 1000.0 / hits_mean
* env
.producer_cnt
);
192 void local_storage_report_progress(int iter
, struct bench_res
*res
,
195 double important_hits_per_sec
, hits_per_sec
;
196 double delta_sec
= delta_ns
/ 1000000000.0;
198 hits_per_sec
= res
->hits
/ 1000000.0 / delta_sec
;
199 important_hits_per_sec
= res
->important_hits
/ 1000000.0 / delta_sec
;
201 printf("Iter %3d (%7.3lfus): ", iter
, (delta_ns
- 1000000000) / 1000.0);
203 printf("hits %8.3lfM/s ", hits_per_sec
);
204 printf("important_hits %8.3lfM/s\n", important_hits_per_sec
);
207 void local_storage_report_final(struct bench_res res
[], int res_cnt
)
209 double important_hits_mean
= 0.0, important_hits_stddev
= 0.0;
210 double hits_mean
= 0.0, hits_stddev
= 0.0;
213 for (i
= 0; i
< res_cnt
; i
++) {
214 hits_mean
+= res
[i
].hits
/ 1000000.0 / (0.0 + res_cnt
);
215 important_hits_mean
+= res
[i
].important_hits
/ 1000000.0 / (0.0 + res_cnt
);
219 for (i
= 0; i
< res_cnt
; i
++) {
220 hits_stddev
+= (hits_mean
- res
[i
].hits
/ 1000000.0) *
221 (hits_mean
- res
[i
].hits
/ 1000000.0) /
223 important_hits_stddev
+=
224 (important_hits_mean
- res
[i
].important_hits
/ 1000000.0) *
225 (important_hits_mean
- res
[i
].important_hits
/ 1000000.0) /
229 hits_stddev
= sqrt(hits_stddev
);
230 important_hits_stddev
= sqrt(important_hits_stddev
);
232 printf("Summary: hits throughput %8.3lf \u00B1 %5.3lf M ops/s, ",
233 hits_mean
, hits_stddev
);
234 printf("hits latency %8.3lf ns/op, ", 1000.0 / hits_mean
);
235 printf("important_hits throughput %8.3lf \u00B1 %5.3lf M ops/s\n",
236 important_hits_mean
, important_hits_stddev
);
239 const char *argp_program_version
= "benchmark";
240 const char *argp_program_bug_address
= "<bpf@vger.kernel.org>";
241 const char argp_program_doc
[] =
242 "benchmark Generic benchmarking framework.\n"
244 "This tool runs benchmarks.\n"
246 "USAGE: benchmark <bench-name>\n"
249 " # run 'count-local' benchmark with 1 producer and 1 consumer\n"
250 " benchmark count-local\n"
251 " # run 'count-local' with 16 producer and 8 consumer thread, pinned to CPUs\n"
252 " benchmark -p16 -c8 -a count-local\n";
255 ARG_PROD_AFFINITY_SET
= 1000,
256 ARG_CONS_AFFINITY_SET
= 1001,
259 static const struct argp_option opts
[] = {
260 { "list", 'l', NULL
, 0, "List available benchmarks"},
261 { "duration", 'd', "SEC", 0, "Duration of benchmark, seconds"},
262 { "warmup", 'w', "SEC", 0, "Warm-up period, seconds"},
263 { "producers", 'p', "NUM", 0, "Number of producer threads"},
264 { "consumers", 'c', "NUM", 0, "Number of consumer threads"},
265 { "verbose", 'v', NULL
, 0, "Verbose debug output"},
266 { "affinity", 'a', NULL
, 0, "Set consumer/producer thread affinity"},
267 { "quiet", 'q', NULL
, 0, "Be more quiet"},
268 { "prod-affinity", ARG_PROD_AFFINITY_SET
, "CPUSET", 0,
269 "Set of CPUs for producer threads; implies --affinity"},
270 { "cons-affinity", ARG_CONS_AFFINITY_SET
, "CPUSET", 0,
271 "Set of CPUs for consumer threads; implies --affinity"},
275 extern struct argp bench_ringbufs_argp
;
276 extern struct argp bench_bloom_map_argp
;
277 extern struct argp bench_bpf_loop_argp
;
278 extern struct argp bench_local_storage_argp
;
279 extern struct argp bench_local_storage_rcu_tasks_trace_argp
;
280 extern struct argp bench_strncmp_argp
;
281 extern struct argp bench_hashmap_lookup_argp
;
282 extern struct argp bench_local_storage_create_argp
;
283 extern struct argp bench_htab_mem_argp
;
284 extern struct argp bench_trigger_batch_argp
;
285 extern struct argp bench_crypto_argp
;
287 static const struct argp_child bench_parsers
[] = {
288 { &bench_ringbufs_argp
, 0, "Ring buffers benchmark", 0 },
289 { &bench_bloom_map_argp
, 0, "Bloom filter map benchmark", 0 },
290 { &bench_bpf_loop_argp
, 0, "bpf_loop helper benchmark", 0 },
291 { &bench_local_storage_argp
, 0, "local_storage benchmark", 0 },
292 { &bench_strncmp_argp
, 0, "bpf_strncmp helper benchmark", 0 },
293 { &bench_local_storage_rcu_tasks_trace_argp
, 0,
294 "local_storage RCU Tasks Trace slowdown benchmark", 0 },
295 { &bench_hashmap_lookup_argp
, 0, "Hashmap lookup benchmark", 0 },
296 { &bench_local_storage_create_argp
, 0, "local-storage-create benchmark", 0 },
297 { &bench_htab_mem_argp
, 0, "hash map memory benchmark", 0 },
298 { &bench_trigger_batch_argp
, 0, "BPF triggering benchmark", 0 },
299 { &bench_crypto_argp
, 0, "bpf crypto benchmark", 0 },
303 /* Make pos_args global, so that we can run argp_parse twice, if necessary */
306 static error_t
parse_arg(int key
, char *arg
, struct argp_state
*state
)
316 env
.duration_sec
= strtol(arg
, NULL
, 10);
317 if (env
.duration_sec
<= 0) {
318 fprintf(stderr
, "Invalid duration: %s\n", arg
);
323 env
.warmup_sec
= strtol(arg
, NULL
, 10);
324 if (env
.warmup_sec
<= 0) {
325 fprintf(stderr
, "Invalid warm-up duration: %s\n", arg
);
330 env
.producer_cnt
= strtol(arg
, NULL
, 10);
331 if (env
.producer_cnt
< 0) {
332 fprintf(stderr
, "Invalid producer count: %s\n", arg
);
337 env
.consumer_cnt
= strtol(arg
, NULL
, 10);
338 if (env
.consumer_cnt
< 0) {
339 fprintf(stderr
, "Invalid consumer count: %s\n", arg
);
349 case ARG_PROD_AFFINITY_SET
:
351 if (parse_num_list(arg
, &env
.prod_cpus
.cpus
,
352 &env
.prod_cpus
.cpus_len
)) {
353 fprintf(stderr
, "Invalid format of CPU set for producers.");
357 case ARG_CONS_AFFINITY_SET
:
359 if (parse_num_list(arg
, &env
.cons_cpus
.cpus
,
360 &env
.cons_cpus
.cpus_len
)) {
361 fprintf(stderr
, "Invalid format of CPU set for consumers.");
368 "Unrecognized positional argument: %s\n", arg
);
371 env
.bench_name
= strdup(arg
);
374 return ARGP_ERR_UNKNOWN
;
379 static void parse_cmdline_args_init(int argc
, char **argv
)
381 static const struct argp argp
= {
384 .doc
= argp_program_doc
,
385 .children
= bench_parsers
,
387 if (argp_parse(&argp
, argc
, argv
, 0, NULL
, NULL
))
391 static void parse_cmdline_args_final(int argc
, char **argv
)
393 struct argp_child bench_parsers
[2] = {};
394 const struct argp argp
= {
397 .doc
= argp_program_doc
,
398 .children
= bench_parsers
,
401 /* Parse arguments the second time with the correct set of parsers */
403 bench_parsers
[0].argp
= bench
->argp
;
404 bench_parsers
[0].header
= bench
->name
;
406 if (argp_parse(&argp
, argc
, argv
, 0, NULL
, NULL
))
411 static void collect_measurements(long delta_ns
);
413 static __u64 last_time_ns
;
414 static void sigalarm_handler(int signo
)
416 long new_time_ns
= get_time_ns();
417 long delta_ns
= new_time_ns
- last_time_ns
;
419 collect_measurements(delta_ns
);
421 last_time_ns
= new_time_ns
;
424 /* set up periodic 1-second timer */
425 static void setup_timer()
427 static struct sigaction sigalarm_action
= {
428 .sa_handler
= sigalarm_handler
,
430 struct itimerval timer_settings
= {};
433 last_time_ns
= get_time_ns();
434 err
= sigaction(SIGALRM
, &sigalarm_action
, NULL
);
436 fprintf(stderr
, "failed to install SIGALRM handler: %d\n", -errno
);
439 timer_settings
.it_interval
.tv_sec
= 1;
440 timer_settings
.it_value
.tv_sec
= 1;
441 err
= setitimer(ITIMER_REAL
, &timer_settings
, NULL
);
443 fprintf(stderr
, "failed to arm interval timer: %d\n", -errno
);
448 static void set_thread_affinity(pthread_t thread
, int cpu
)
454 CPU_SET(cpu
, &cpuset
);
455 err
= pthread_setaffinity_np(thread
, sizeof(cpuset
), &cpuset
);
457 fprintf(stderr
, "setting affinity to CPU #%d failed: %d\n",
463 static int next_cpu(struct cpu_set
*cpu_set
)
468 /* find next available CPU */
469 for (i
= cpu_set
->next_cpu
; i
< cpu_set
->cpus_len
; i
++) {
470 if (cpu_set
->cpus
[i
]) {
471 cpu_set
->next_cpu
= i
+ 1;
475 fprintf(stderr
, "Not enough CPUs specified, need CPU #%d or higher.\n", i
);
479 return cpu_set
->next_cpu
++ % env
.nr_cpus
;
482 static struct bench_state
{
484 struct bench_res
*results
;
485 pthread_t
*consumers
;
486 pthread_t
*producers
;
489 const struct bench
*bench
= NULL
;
491 extern const struct bench bench_count_global
;
492 extern const struct bench bench_count_local
;
493 extern const struct bench bench_rename_base
;
494 extern const struct bench bench_rename_kprobe
;
495 extern const struct bench bench_rename_kretprobe
;
496 extern const struct bench bench_rename_rawtp
;
497 extern const struct bench bench_rename_fentry
;
498 extern const struct bench bench_rename_fexit
;
500 /* pure counting benchmarks to establish theoretical lmits */
501 extern const struct bench bench_trig_usermode_count
;
502 extern const struct bench bench_trig_syscall_count
;
503 extern const struct bench bench_trig_kernel_count
;
505 /* batched, staying mostly in-kernel benchmarks */
506 extern const struct bench bench_trig_kprobe
;
507 extern const struct bench bench_trig_kretprobe
;
508 extern const struct bench bench_trig_kprobe_multi
;
509 extern const struct bench bench_trig_kretprobe_multi
;
510 extern const struct bench bench_trig_fentry
;
511 extern const struct bench bench_trig_fexit
;
512 extern const struct bench bench_trig_fmodret
;
513 extern const struct bench bench_trig_tp
;
514 extern const struct bench bench_trig_rawtp
;
516 /* uprobe/uretprobe benchmarks */
517 extern const struct bench bench_trig_uprobe_nop
;
518 extern const struct bench bench_trig_uretprobe_nop
;
519 extern const struct bench bench_trig_uprobe_push
;
520 extern const struct bench bench_trig_uretprobe_push
;
521 extern const struct bench bench_trig_uprobe_ret
;
522 extern const struct bench bench_trig_uretprobe_ret
;
523 extern const struct bench bench_trig_uprobe_multi_nop
;
524 extern const struct bench bench_trig_uretprobe_multi_nop
;
525 extern const struct bench bench_trig_uprobe_multi_push
;
526 extern const struct bench bench_trig_uretprobe_multi_push
;
527 extern const struct bench bench_trig_uprobe_multi_ret
;
528 extern const struct bench bench_trig_uretprobe_multi_ret
;
530 extern const struct bench bench_rb_libbpf
;
531 extern const struct bench bench_rb_custom
;
532 extern const struct bench bench_pb_libbpf
;
533 extern const struct bench bench_pb_custom
;
534 extern const struct bench bench_bloom_lookup
;
535 extern const struct bench bench_bloom_update
;
536 extern const struct bench bench_bloom_false_positive
;
537 extern const struct bench bench_hashmap_without_bloom
;
538 extern const struct bench bench_hashmap_with_bloom
;
539 extern const struct bench bench_bpf_loop
;
540 extern const struct bench bench_strncmp_no_helper
;
541 extern const struct bench bench_strncmp_helper
;
542 extern const struct bench bench_bpf_hashmap_full_update
;
543 extern const struct bench bench_local_storage_cache_seq_get
;
544 extern const struct bench bench_local_storage_cache_interleaved_get
;
545 extern const struct bench bench_local_storage_cache_hashmap_control
;
546 extern const struct bench bench_local_storage_tasks_trace
;
547 extern const struct bench bench_bpf_hashmap_lookup
;
548 extern const struct bench bench_local_storage_create
;
549 extern const struct bench bench_htab_mem
;
550 extern const struct bench bench_crypto_encrypt
;
551 extern const struct bench bench_crypto_decrypt
;
553 static const struct bench
*benchs
[] = {
557 &bench_rename_kprobe
,
558 &bench_rename_kretprobe
,
560 &bench_rename_fentry
,
562 /* pure counting benchmarks for establishing theoretical limits */
563 &bench_trig_usermode_count
,
564 &bench_trig_kernel_count
,
565 &bench_trig_syscall_count
,
566 /* batched, staying mostly in-kernel triggers */
568 &bench_trig_kretprobe
,
569 &bench_trig_kprobe_multi
,
570 &bench_trig_kretprobe_multi
,
577 &bench_trig_uprobe_nop
,
578 &bench_trig_uretprobe_nop
,
579 &bench_trig_uprobe_push
,
580 &bench_trig_uretprobe_push
,
581 &bench_trig_uprobe_ret
,
582 &bench_trig_uretprobe_ret
,
583 &bench_trig_uprobe_multi_nop
,
584 &bench_trig_uretprobe_multi_nop
,
585 &bench_trig_uprobe_multi_push
,
586 &bench_trig_uretprobe_multi_push
,
587 &bench_trig_uprobe_multi_ret
,
588 &bench_trig_uretprobe_multi_ret
,
589 /* ringbuf/perfbuf benchmarks */
596 &bench_bloom_false_positive
,
597 &bench_hashmap_without_bloom
,
598 &bench_hashmap_with_bloom
,
600 &bench_strncmp_no_helper
,
601 &bench_strncmp_helper
,
602 &bench_bpf_hashmap_full_update
,
603 &bench_local_storage_cache_seq_get
,
604 &bench_local_storage_cache_interleaved_get
,
605 &bench_local_storage_cache_hashmap_control
,
606 &bench_local_storage_tasks_trace
,
607 &bench_bpf_hashmap_lookup
,
608 &bench_local_storage_create
,
610 &bench_crypto_encrypt
,
611 &bench_crypto_decrypt
,
614 static void find_benchmark(void)
618 if (!env
.bench_name
) {
619 fprintf(stderr
, "benchmark name is not specified\n");
622 for (i
= 0; i
< ARRAY_SIZE(benchs
); i
++) {
623 if (strcmp(benchs
[i
]->name
, env
.bench_name
) == 0) {
629 fprintf(stderr
, "benchmark '%s' not found\n", env
.bench_name
);
634 static void setup_benchmark(void)
639 printf("Setting up benchmark '%s'...\n", bench
->name
);
641 state
.producers
= calloc(env
.producer_cnt
, sizeof(*state
.producers
));
642 state
.consumers
= calloc(env
.consumer_cnt
, sizeof(*state
.consumers
));
643 state
.results
= calloc(env
.duration_sec
+ env
.warmup_sec
+ 2,
644 sizeof(*state
.results
));
645 if (!state
.producers
|| !state
.consumers
|| !state
.results
)
653 for (i
= 0; i
< env
.consumer_cnt
; i
++) {
654 if (!bench
->consumer_thread
) {
655 fprintf(stderr
, "benchmark doesn't support consumers!\n");
658 err
= pthread_create(&state
.consumers
[i
], NULL
,
659 bench
->consumer_thread
, (void *)(long)i
);
661 fprintf(stderr
, "failed to create consumer thread #%d: %d\n",
666 set_thread_affinity(state
.consumers
[i
],
667 next_cpu(&env
.cons_cpus
));
670 /* unless explicit producer CPU list is specified, continue after
673 if (!env
.prod_cpus
.cpus
)
674 env
.prod_cpus
.next_cpu
= env
.cons_cpus
.next_cpu
;
676 for (i
= 0; i
< env
.producer_cnt
; i
++) {
677 if (!bench
->producer_thread
) {
678 fprintf(stderr
, "benchmark doesn't support producers!\n");
681 err
= pthread_create(&state
.producers
[i
], NULL
,
682 bench
->producer_thread
, (void *)(long)i
);
684 fprintf(stderr
, "failed to create producer thread #%d: %d\n",
689 set_thread_affinity(state
.producers
[i
],
690 next_cpu(&env
.prod_cpus
));
694 printf("Benchmark '%s' started.\n", bench
->name
);
697 static pthread_mutex_t bench_done_mtx
= PTHREAD_MUTEX_INITIALIZER
;
698 static pthread_cond_t bench_done
= PTHREAD_COND_INITIALIZER
;
700 static void collect_measurements(long delta_ns
) {
701 int iter
= state
.res_cnt
++;
702 struct bench_res
*res
= &state
.results
[iter
];
706 if (bench
->report_progress
)
707 bench
->report_progress(iter
, res
, delta_ns
);
709 if (iter
== env
.duration_sec
+ env
.warmup_sec
) {
710 pthread_mutex_lock(&bench_done_mtx
);
711 pthread_cond_signal(&bench_done
);
712 pthread_mutex_unlock(&bench_done_mtx
);
716 int main(int argc
, char **argv
)
718 env
.nr_cpus
= get_nprocs();
719 parse_cmdline_args_init(argc
, argv
);
724 printf("Available benchmarks:\n");
725 for (i
= 0; i
< ARRAY_SIZE(benchs
); i
++) {
726 printf("- %s\n", benchs
[i
]->name
);
732 parse_cmdline_args_final(argc
, argv
);
738 pthread_mutex_lock(&bench_done_mtx
);
739 pthread_cond_wait(&bench_done
, &bench_done_mtx
);
740 pthread_mutex_unlock(&bench_done_mtx
);
742 if (bench
->report_final
)
743 /* skip first sample */
744 bench
->report_final(state
.results
+ env
.warmup_sec
,
745 state
.res_cnt
- env
.warmup_sec
);