0001
0002
0003 #define _GNU_SOURCE
0004 #include <argp.h>
0005 #include <linux/compiler.h>
0006 #include <sys/time.h>
0007 #include <sched.h>
0008 #include <fcntl.h>
0009 #include <pthread.h>
0010 #include <sys/sysinfo.h>
0011 #include <signal.h>
0012 #include "bench.h"
0013 #include "testing_helpers.h"
0014
0015 struct env env = {
0016 .warmup_sec = 1,
0017 .duration_sec = 5,
0018 .affinity = false,
0019 .consumer_cnt = 1,
0020 .producer_cnt = 1,
0021 };
0022
0023 static int libbpf_print_fn(enum libbpf_print_level level,
0024 const char *format, va_list args)
0025 {
0026 if (level == LIBBPF_DEBUG && !env.verbose)
0027 return 0;
0028 return vfprintf(stderr, format, args);
0029 }
0030
0031 void setup_libbpf(void)
0032 {
0033 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
0034 libbpf_set_print(libbpf_print_fn);
0035 }
0036
0037 void false_hits_report_progress(int iter, struct bench_res *res, long delta_ns)
0038 {
0039 long total = res->false_hits + res->hits + res->drops;
0040
0041 printf("Iter %3d (%7.3lfus): ",
0042 iter, (delta_ns - 1000000000) / 1000.0);
0043
0044 printf("%ld false hits of %ld total operations. Percentage = %2.2f %%\n",
0045 res->false_hits, total, ((float)res->false_hits / total) * 100);
0046 }
0047
0048 void false_hits_report_final(struct bench_res res[], int res_cnt)
0049 {
0050 long total_hits = 0, total_drops = 0, total_false_hits = 0, total_ops = 0;
0051 int i;
0052
0053 for (i = 0; i < res_cnt; i++) {
0054 total_hits += res[i].hits;
0055 total_false_hits += res[i].false_hits;
0056 total_drops += res[i].drops;
0057 }
0058 total_ops = total_hits + total_false_hits + total_drops;
0059
0060 printf("Summary: %ld false hits of %ld total operations. ",
0061 total_false_hits, total_ops);
0062 printf("Percentage = %2.2f %%\n",
0063 ((float)total_false_hits / total_ops) * 100);
0064 }
0065
0066 void hits_drops_report_progress(int iter, struct bench_res *res, long delta_ns)
0067 {
0068 double hits_per_sec, drops_per_sec;
0069 double hits_per_prod;
0070
0071 hits_per_sec = res->hits / 1000000.0 / (delta_ns / 1000000000.0);
0072 hits_per_prod = hits_per_sec / env.producer_cnt;
0073 drops_per_sec = res->drops / 1000000.0 / (delta_ns / 1000000000.0);
0074
0075 printf("Iter %3d (%7.3lfus): ",
0076 iter, (delta_ns - 1000000000) / 1000.0);
0077
0078 printf("hits %8.3lfM/s (%7.3lfM/prod), drops %8.3lfM/s, total operations %8.3lfM/s\n",
0079 hits_per_sec, hits_per_prod, drops_per_sec, hits_per_sec + drops_per_sec);
0080 }
0081
0082 void
0083 grace_period_latency_basic_stats(struct bench_res res[], int res_cnt, struct basic_stats *gp_stat)
0084 {
0085 int i;
0086
0087 memset(gp_stat, 0, sizeof(struct basic_stats));
0088
0089 for (i = 0; i < res_cnt; i++)
0090 gp_stat->mean += res[i].gp_ns / 1000.0 / (double)res[i].gp_ct / (0.0 + res_cnt);
0091
0092 #define IT_MEAN_DIFF (res[i].gp_ns / 1000.0 / (double)res[i].gp_ct - gp_stat->mean)
0093 if (res_cnt > 1) {
0094 for (i = 0; i < res_cnt; i++)
0095 gp_stat->stddev += (IT_MEAN_DIFF * IT_MEAN_DIFF) / (res_cnt - 1.0);
0096 }
0097 gp_stat->stddev = sqrt(gp_stat->stddev);
0098 #undef IT_MEAN_DIFF
0099 }
0100
0101 void
0102 grace_period_ticks_basic_stats(struct bench_res res[], int res_cnt, struct basic_stats *gp_stat)
0103 {
0104 int i;
0105
0106 memset(gp_stat, 0, sizeof(struct basic_stats));
0107 for (i = 0; i < res_cnt; i++)
0108 gp_stat->mean += res[i].stime / (double)res[i].gp_ct / (0.0 + res_cnt);
0109
0110 #define IT_MEAN_DIFF (res[i].stime / (double)res[i].gp_ct - gp_stat->mean)
0111 if (res_cnt > 1) {
0112 for (i = 0; i < res_cnt; i++)
0113 gp_stat->stddev += (IT_MEAN_DIFF * IT_MEAN_DIFF) / (res_cnt - 1.0);
0114 }
0115 gp_stat->stddev = sqrt(gp_stat->stddev);
0116 #undef IT_MEAN_DIFF
0117 }
0118
0119 void hits_drops_report_final(struct bench_res res[], int res_cnt)
0120 {
0121 int i;
0122 double hits_mean = 0.0, drops_mean = 0.0, total_ops_mean = 0.0;
0123 double hits_stddev = 0.0, drops_stddev = 0.0, total_ops_stddev = 0.0;
0124 double total_ops;
0125
0126 for (i = 0; i < res_cnt; i++) {
0127 hits_mean += res[i].hits / 1000000.0 / (0.0 + res_cnt);
0128 drops_mean += res[i].drops / 1000000.0 / (0.0 + res_cnt);
0129 }
0130 total_ops_mean = hits_mean + drops_mean;
0131
0132 if (res_cnt > 1) {
0133 for (i = 0; i < res_cnt; i++) {
0134 hits_stddev += (hits_mean - res[i].hits / 1000000.0) *
0135 (hits_mean - res[i].hits / 1000000.0) /
0136 (res_cnt - 1.0);
0137 drops_stddev += (drops_mean - res[i].drops / 1000000.0) *
0138 (drops_mean - res[i].drops / 1000000.0) /
0139 (res_cnt - 1.0);
0140 total_ops = res[i].hits + res[i].drops;
0141 total_ops_stddev += (total_ops_mean - total_ops / 1000000.0) *
0142 (total_ops_mean - total_ops / 1000000.0) /
0143 (res_cnt - 1.0);
0144 }
0145 hits_stddev = sqrt(hits_stddev);
0146 drops_stddev = sqrt(drops_stddev);
0147 total_ops_stddev = sqrt(total_ops_stddev);
0148 }
0149 printf("Summary: hits %8.3lf \u00B1 %5.3lfM/s (%7.3lfM/prod), ",
0150 hits_mean, hits_stddev, hits_mean / env.producer_cnt);
0151 printf("drops %8.3lf \u00B1 %5.3lfM/s, ",
0152 drops_mean, drops_stddev);
0153 printf("total operations %8.3lf \u00B1 %5.3lfM/s\n",
0154 total_ops_mean, total_ops_stddev);
0155 }
0156
0157 void ops_report_progress(int iter, struct bench_res *res, long delta_ns)
0158 {
0159 double hits_per_sec, hits_per_prod;
0160
0161 hits_per_sec = res->hits / 1000000.0 / (delta_ns / 1000000000.0);
0162 hits_per_prod = hits_per_sec / env.producer_cnt;
0163
0164 printf("Iter %3d (%7.3lfus): ", iter, (delta_ns - 1000000000) / 1000.0);
0165
0166 printf("hits %8.3lfM/s (%7.3lfM/prod)\n", hits_per_sec, hits_per_prod);
0167 }
0168
0169 void ops_report_final(struct bench_res res[], int res_cnt)
0170 {
0171 double hits_mean = 0.0, hits_stddev = 0.0;
0172 int i;
0173
0174 for (i = 0; i < res_cnt; i++)
0175 hits_mean += res[i].hits / 1000000.0 / (0.0 + res_cnt);
0176
0177 if (res_cnt > 1) {
0178 for (i = 0; i < res_cnt; i++)
0179 hits_stddev += (hits_mean - res[i].hits / 1000000.0) *
0180 (hits_mean - res[i].hits / 1000000.0) /
0181 (res_cnt - 1.0);
0182
0183 hits_stddev = sqrt(hits_stddev);
0184 }
0185 printf("Summary: throughput %8.3lf \u00B1 %5.3lf M ops/s (%7.3lfM ops/prod), ",
0186 hits_mean, hits_stddev, hits_mean / env.producer_cnt);
0187 printf("latency %8.3lf ns/op\n", 1000.0 / hits_mean * env.producer_cnt);
0188 }
0189
0190 void local_storage_report_progress(int iter, struct bench_res *res,
0191 long delta_ns)
0192 {
0193 double important_hits_per_sec, hits_per_sec;
0194 double delta_sec = delta_ns / 1000000000.0;
0195
0196 hits_per_sec = res->hits / 1000000.0 / delta_sec;
0197 important_hits_per_sec = res->important_hits / 1000000.0 / delta_sec;
0198
0199 printf("Iter %3d (%7.3lfus): ", iter, (delta_ns - 1000000000) / 1000.0);
0200
0201 printf("hits %8.3lfM/s ", hits_per_sec);
0202 printf("important_hits %8.3lfM/s\n", important_hits_per_sec);
0203 }
0204
0205 void local_storage_report_final(struct bench_res res[], int res_cnt)
0206 {
0207 double important_hits_mean = 0.0, important_hits_stddev = 0.0;
0208 double hits_mean = 0.0, hits_stddev = 0.0;
0209 int i;
0210
0211 for (i = 0; i < res_cnt; i++) {
0212 hits_mean += res[i].hits / 1000000.0 / (0.0 + res_cnt);
0213 important_hits_mean += res[i].important_hits / 1000000.0 / (0.0 + res_cnt);
0214 }
0215
0216 if (res_cnt > 1) {
0217 for (i = 0; i < res_cnt; i++) {
0218 hits_stddev += (hits_mean - res[i].hits / 1000000.0) *
0219 (hits_mean - res[i].hits / 1000000.0) /
0220 (res_cnt - 1.0);
0221 important_hits_stddev +=
0222 (important_hits_mean - res[i].important_hits / 1000000.0) *
0223 (important_hits_mean - res[i].important_hits / 1000000.0) /
0224 (res_cnt - 1.0);
0225 }
0226
0227 hits_stddev = sqrt(hits_stddev);
0228 important_hits_stddev = sqrt(important_hits_stddev);
0229 }
0230 printf("Summary: hits throughput %8.3lf \u00B1 %5.3lf M ops/s, ",
0231 hits_mean, hits_stddev);
0232 printf("hits latency %8.3lf ns/op, ", 1000.0 / hits_mean);
0233 printf("important_hits throughput %8.3lf \u00B1 %5.3lf M ops/s\n",
0234 important_hits_mean, important_hits_stddev);
0235 }
0236
0237 const char *argp_program_version = "benchmark";
0238 const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
0239 const char argp_program_doc[] =
0240 "benchmark Generic benchmarking framework.\n"
0241 "\n"
0242 "This tool runs benchmarks.\n"
0243 "\n"
0244 "USAGE: benchmark <bench-name>\n"
0245 "\n"
0246 "EXAMPLES:\n"
0247 " # run 'count-local' benchmark with 1 producer and 1 consumer\n"
0248 " benchmark count-local\n"
0249 " # run 'count-local' with 16 producer and 8 consumer thread, pinned to CPUs\n"
0250 " benchmark -p16 -c8 -a count-local\n";
0251
0252 enum {
0253 ARG_PROD_AFFINITY_SET = 1000,
0254 ARG_CONS_AFFINITY_SET = 1001,
0255 };
0256
0257 static const struct argp_option opts[] = {
0258 { "list", 'l', NULL, 0, "List available benchmarks"},
0259 { "duration", 'd', "SEC", 0, "Duration of benchmark, seconds"},
0260 { "warmup", 'w', "SEC", 0, "Warm-up period, seconds"},
0261 { "producers", 'p', "NUM", 0, "Number of producer threads"},
0262 { "consumers", 'c', "NUM", 0, "Number of consumer threads"},
0263 { "verbose", 'v', NULL, 0, "Verbose debug output"},
0264 { "affinity", 'a', NULL, 0, "Set consumer/producer thread affinity"},
0265 { "prod-affinity", ARG_PROD_AFFINITY_SET, "CPUSET", 0,
0266 "Set of CPUs for producer threads; implies --affinity"},
0267 { "cons-affinity", ARG_CONS_AFFINITY_SET, "CPUSET", 0,
0268 "Set of CPUs for consumer threads; implies --affinity"},
0269 {},
0270 };
0271
0272 extern struct argp bench_ringbufs_argp;
0273 extern struct argp bench_bloom_map_argp;
0274 extern struct argp bench_bpf_loop_argp;
0275 extern struct argp bench_local_storage_argp;
0276 extern struct argp bench_local_storage_rcu_tasks_trace_argp;
0277 extern struct argp bench_strncmp_argp;
0278
0279 static const struct argp_child bench_parsers[] = {
0280 { &bench_ringbufs_argp, 0, "Ring buffers benchmark", 0 },
0281 { &bench_bloom_map_argp, 0, "Bloom filter map benchmark", 0 },
0282 { &bench_bpf_loop_argp, 0, "bpf_loop helper benchmark", 0 },
0283 { &bench_local_storage_argp, 0, "local_storage benchmark", 0 },
0284 { &bench_strncmp_argp, 0, "bpf_strncmp helper benchmark", 0 },
0285 { &bench_local_storage_rcu_tasks_trace_argp, 0,
0286 "local_storage RCU Tasks Trace slowdown benchmark", 0 },
0287 {},
0288 };
0289
0290 static error_t parse_arg(int key, char *arg, struct argp_state *state)
0291 {
0292 static int pos_args;
0293
0294 switch (key) {
0295 case 'v':
0296 env.verbose = true;
0297 break;
0298 case 'l':
0299 env.list = true;
0300 break;
0301 case 'd':
0302 env.duration_sec = strtol(arg, NULL, 10);
0303 if (env.duration_sec <= 0) {
0304 fprintf(stderr, "Invalid duration: %s\n", arg);
0305 argp_usage(state);
0306 }
0307 break;
0308 case 'w':
0309 env.warmup_sec = strtol(arg, NULL, 10);
0310 if (env.warmup_sec <= 0) {
0311 fprintf(stderr, "Invalid warm-up duration: %s\n", arg);
0312 argp_usage(state);
0313 }
0314 break;
0315 case 'p':
0316 env.producer_cnt = strtol(arg, NULL, 10);
0317 if (env.producer_cnt <= 0) {
0318 fprintf(stderr, "Invalid producer count: %s\n", arg);
0319 argp_usage(state);
0320 }
0321 break;
0322 case 'c':
0323 env.consumer_cnt = strtol(arg, NULL, 10);
0324 if (env.consumer_cnt <= 0) {
0325 fprintf(stderr, "Invalid consumer count: %s\n", arg);
0326 argp_usage(state);
0327 }
0328 break;
0329 case 'a':
0330 env.affinity = true;
0331 break;
0332 case ARG_PROD_AFFINITY_SET:
0333 env.affinity = true;
0334 if (parse_num_list(arg, &env.prod_cpus.cpus,
0335 &env.prod_cpus.cpus_len)) {
0336 fprintf(stderr, "Invalid format of CPU set for producers.");
0337 argp_usage(state);
0338 }
0339 break;
0340 case ARG_CONS_AFFINITY_SET:
0341 env.affinity = true;
0342 if (parse_num_list(arg, &env.cons_cpus.cpus,
0343 &env.cons_cpus.cpus_len)) {
0344 fprintf(stderr, "Invalid format of CPU set for consumers.");
0345 argp_usage(state);
0346 }
0347 break;
0348 case ARGP_KEY_ARG:
0349 if (pos_args++) {
0350 fprintf(stderr,
0351 "Unrecognized positional argument: %s\n", arg);
0352 argp_usage(state);
0353 }
0354 env.bench_name = strdup(arg);
0355 break;
0356 default:
0357 return ARGP_ERR_UNKNOWN;
0358 }
0359 return 0;
0360 }
0361
0362 static void parse_cmdline_args(int argc, char **argv)
0363 {
0364 static const struct argp argp = {
0365 .options = opts,
0366 .parser = parse_arg,
0367 .doc = argp_program_doc,
0368 .children = bench_parsers,
0369 };
0370 if (argp_parse(&argp, argc, argv, 0, NULL, NULL))
0371 exit(1);
0372 if (!env.list && !env.bench_name) {
0373 argp_help(&argp, stderr, ARGP_HELP_DOC, "bench");
0374 exit(1);
0375 }
0376 }
0377
0378 static void collect_measurements(long delta_ns);
0379
0380 static __u64 last_time_ns;
0381 static void sigalarm_handler(int signo)
0382 {
0383 long new_time_ns = get_time_ns();
0384 long delta_ns = new_time_ns - last_time_ns;
0385
0386 collect_measurements(delta_ns);
0387
0388 last_time_ns = new_time_ns;
0389 }
0390
0391
0392 static void setup_timer()
0393 {
0394 static struct sigaction sigalarm_action = {
0395 .sa_handler = sigalarm_handler,
0396 };
0397 struct itimerval timer_settings = {};
0398 int err;
0399
0400 last_time_ns = get_time_ns();
0401 err = sigaction(SIGALRM, &sigalarm_action, NULL);
0402 if (err < 0) {
0403 fprintf(stderr, "failed to install SIGALRM handler: %d\n", -errno);
0404 exit(1);
0405 }
0406 timer_settings.it_interval.tv_sec = 1;
0407 timer_settings.it_value.tv_sec = 1;
0408 err = setitimer(ITIMER_REAL, &timer_settings, NULL);
0409 if (err < 0) {
0410 fprintf(stderr, "failed to arm interval timer: %d\n", -errno);
0411 exit(1);
0412 }
0413 }
0414
0415 static void set_thread_affinity(pthread_t thread, int cpu)
0416 {
0417 cpu_set_t cpuset;
0418
0419 CPU_ZERO(&cpuset);
0420 CPU_SET(cpu, &cpuset);
0421 if (pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset)) {
0422 fprintf(stderr, "setting affinity to CPU #%d failed: %d\n",
0423 cpu, errno);
0424 exit(1);
0425 }
0426 }
0427
0428 static int next_cpu(struct cpu_set *cpu_set)
0429 {
0430 if (cpu_set->cpus) {
0431 int i;
0432
0433
0434 for (i = cpu_set->next_cpu; i < cpu_set->cpus_len; i++) {
0435 if (cpu_set->cpus[i]) {
0436 cpu_set->next_cpu = i + 1;
0437 return i;
0438 }
0439 }
0440 fprintf(stderr, "Not enough CPUs specified, need CPU #%d or higher.\n", i);
0441 exit(1);
0442 }
0443
0444 return cpu_set->next_cpu++;
0445 }
0446
0447 static struct bench_state {
0448 int res_cnt;
0449 struct bench_res *results;
0450 pthread_t *consumers;
0451 pthread_t *producers;
0452 } state;
0453
0454 const struct bench *bench = NULL;
0455
0456 extern const struct bench bench_count_global;
0457 extern const struct bench bench_count_local;
0458 extern const struct bench bench_rename_base;
0459 extern const struct bench bench_rename_kprobe;
0460 extern const struct bench bench_rename_kretprobe;
0461 extern const struct bench bench_rename_rawtp;
0462 extern const struct bench bench_rename_fentry;
0463 extern const struct bench bench_rename_fexit;
0464 extern const struct bench bench_trig_base;
0465 extern const struct bench bench_trig_tp;
0466 extern const struct bench bench_trig_rawtp;
0467 extern const struct bench bench_trig_kprobe;
0468 extern const struct bench bench_trig_fentry;
0469 extern const struct bench bench_trig_fentry_sleep;
0470 extern const struct bench bench_trig_fmodret;
0471 extern const struct bench bench_trig_uprobe_base;
0472 extern const struct bench bench_trig_uprobe_with_nop;
0473 extern const struct bench bench_trig_uretprobe_with_nop;
0474 extern const struct bench bench_trig_uprobe_without_nop;
0475 extern const struct bench bench_trig_uretprobe_without_nop;
0476 extern const struct bench bench_rb_libbpf;
0477 extern const struct bench bench_rb_custom;
0478 extern const struct bench bench_pb_libbpf;
0479 extern const struct bench bench_pb_custom;
0480 extern const struct bench bench_bloom_lookup;
0481 extern const struct bench bench_bloom_update;
0482 extern const struct bench bench_bloom_false_positive;
0483 extern const struct bench bench_hashmap_without_bloom;
0484 extern const struct bench bench_hashmap_with_bloom;
0485 extern const struct bench bench_bpf_loop;
0486 extern const struct bench bench_strncmp_no_helper;
0487 extern const struct bench bench_strncmp_helper;
0488 extern const struct bench bench_bpf_hashmap_full_update;
0489 extern const struct bench bench_local_storage_cache_seq_get;
0490 extern const struct bench bench_local_storage_cache_interleaved_get;
0491 extern const struct bench bench_local_storage_cache_hashmap_control;
0492 extern const struct bench bench_local_storage_tasks_trace;
0493
0494 static const struct bench *benchs[] = {
0495 &bench_count_global,
0496 &bench_count_local,
0497 &bench_rename_base,
0498 &bench_rename_kprobe,
0499 &bench_rename_kretprobe,
0500 &bench_rename_rawtp,
0501 &bench_rename_fentry,
0502 &bench_rename_fexit,
0503 &bench_trig_base,
0504 &bench_trig_tp,
0505 &bench_trig_rawtp,
0506 &bench_trig_kprobe,
0507 &bench_trig_fentry,
0508 &bench_trig_fentry_sleep,
0509 &bench_trig_fmodret,
0510 &bench_trig_uprobe_base,
0511 &bench_trig_uprobe_with_nop,
0512 &bench_trig_uretprobe_with_nop,
0513 &bench_trig_uprobe_without_nop,
0514 &bench_trig_uretprobe_without_nop,
0515 &bench_rb_libbpf,
0516 &bench_rb_custom,
0517 &bench_pb_libbpf,
0518 &bench_pb_custom,
0519 &bench_bloom_lookup,
0520 &bench_bloom_update,
0521 &bench_bloom_false_positive,
0522 &bench_hashmap_without_bloom,
0523 &bench_hashmap_with_bloom,
0524 &bench_bpf_loop,
0525 &bench_strncmp_no_helper,
0526 &bench_strncmp_helper,
0527 &bench_bpf_hashmap_full_update,
0528 &bench_local_storage_cache_seq_get,
0529 &bench_local_storage_cache_interleaved_get,
0530 &bench_local_storage_cache_hashmap_control,
0531 &bench_local_storage_tasks_trace,
0532 };
0533
0534 static void setup_benchmark()
0535 {
0536 int i, err;
0537
0538 if (!env.bench_name) {
0539 fprintf(stderr, "benchmark name is not specified\n");
0540 exit(1);
0541 }
0542
0543 for (i = 0; i < ARRAY_SIZE(benchs); i++) {
0544 if (strcmp(benchs[i]->name, env.bench_name) == 0) {
0545 bench = benchs[i];
0546 break;
0547 }
0548 }
0549 if (!bench) {
0550 fprintf(stderr, "benchmark '%s' not found\n", env.bench_name);
0551 exit(1);
0552 }
0553
0554 printf("Setting up benchmark '%s'...\n", bench->name);
0555
0556 state.producers = calloc(env.producer_cnt, sizeof(*state.producers));
0557 state.consumers = calloc(env.consumer_cnt, sizeof(*state.consumers));
0558 state.results = calloc(env.duration_sec + env.warmup_sec + 2,
0559 sizeof(*state.results));
0560 if (!state.producers || !state.consumers || !state.results)
0561 exit(1);
0562
0563 if (bench->validate)
0564 bench->validate();
0565 if (bench->setup)
0566 bench->setup();
0567
0568 for (i = 0; i < env.consumer_cnt; i++) {
0569 err = pthread_create(&state.consumers[i], NULL,
0570 bench->consumer_thread, (void *)(long)i);
0571 if (err) {
0572 fprintf(stderr, "failed to create consumer thread #%d: %d\n",
0573 i, -errno);
0574 exit(1);
0575 }
0576 if (env.affinity)
0577 set_thread_affinity(state.consumers[i],
0578 next_cpu(&env.cons_cpus));
0579 }
0580
0581
0582
0583
0584 if (!env.prod_cpus.cpus)
0585 env.prod_cpus.next_cpu = env.cons_cpus.next_cpu;
0586
0587 for (i = 0; i < env.producer_cnt; i++) {
0588 err = pthread_create(&state.producers[i], NULL,
0589 bench->producer_thread, (void *)(long)i);
0590 if (err) {
0591 fprintf(stderr, "failed to create producer thread #%d: %d\n",
0592 i, -errno);
0593 exit(1);
0594 }
0595 if (env.affinity)
0596 set_thread_affinity(state.producers[i],
0597 next_cpu(&env.prod_cpus));
0598 }
0599
0600 printf("Benchmark '%s' started.\n", bench->name);
0601 }
0602
0603 static pthread_mutex_t bench_done_mtx = PTHREAD_MUTEX_INITIALIZER;
0604 static pthread_cond_t bench_done = PTHREAD_COND_INITIALIZER;
0605
0606 static void collect_measurements(long delta_ns) {
0607 int iter = state.res_cnt++;
0608 struct bench_res *res = &state.results[iter];
0609
0610 bench->measure(res);
0611
0612 if (bench->report_progress)
0613 bench->report_progress(iter, res, delta_ns);
0614
0615 if (iter == env.duration_sec + env.warmup_sec) {
0616 pthread_mutex_lock(&bench_done_mtx);
0617 pthread_cond_signal(&bench_done);
0618 pthread_mutex_unlock(&bench_done_mtx);
0619 }
0620 }
0621
0622 int main(int argc, char **argv)
0623 {
0624 parse_cmdline_args(argc, argv);
0625
0626 if (env.list) {
0627 int i;
0628
0629 printf("Available benchmarks:\n");
0630 for (i = 0; i < ARRAY_SIZE(benchs); i++) {
0631 printf("- %s\n", benchs[i]->name);
0632 }
0633 return 0;
0634 }
0635
0636 setup_benchmark();
0637
0638 setup_timer();
0639
0640 pthread_mutex_lock(&bench_done_mtx);
0641 pthread_cond_wait(&bench_done, &bench_done_mtx);
0642 pthread_mutex_unlock(&bench_done_mtx);
0643
0644 if (bench->report_final)
0645
0646 bench->report_final(state.results + env.warmup_sec,
0647 state.res_cnt - env.warmup_sec);
0648
0649 return 0;
0650 }