1*19c3b8c2SApple OSS Distributions /* Per-cpu counter microbenchmarks. */
2*19c3b8c2SApple OSS Distributions
3*19c3b8c2SApple OSS Distributions #include <assert.h>
4*19c3b8c2SApple OSS Distributions #include <inttypes.h>
5*19c3b8c2SApple OSS Distributions #include <pthread.h>
6*19c3b8c2SApple OSS Distributions #include <stdatomic.h>
7*19c3b8c2SApple OSS Distributions #include <stdbool.h>
8*19c3b8c2SApple OSS Distributions #include <stdio.h>
9*19c3b8c2SApple OSS Distributions #include <stdlib.h>
10*19c3b8c2SApple OSS Distributions #include <string.h>
11*19c3b8c2SApple OSS Distributions
12*19c3b8c2SApple OSS Distributions #include <sys/types.h>
13*19c3b8c2SApple OSS Distributions #include <sys/sysctl.h>
14*19c3b8c2SApple OSS Distributions
15*19c3b8c2SApple OSS Distributions #include "benchmark/helpers.h"
16*19c3b8c2SApple OSS Distributions #include "counter/common.h"
17*19c3b8c2SApple OSS Distributions
18*19c3b8c2SApple OSS Distributions typedef enum test_variant {
19*19c3b8c2SApple OSS Distributions VARIANT_SCALABLE_COUNTER,
20*19c3b8c2SApple OSS Distributions VARIANT_ATOMIC,
21*19c3b8c2SApple OSS Distributions VARIANT_RACY
22*19c3b8c2SApple OSS Distributions } test_variant_t;
23*19c3b8c2SApple OSS Distributions
24*19c3b8c2SApple OSS Distributions static const char* kScalableCounterArgument = "scalable";
25*19c3b8c2SApple OSS Distributions static const char* kAtomicCounterArgument = "atomic";
26*19c3b8c2SApple OSS Distributions static const char* kRacyCounterArgument = "racy";
27*19c3b8c2SApple OSS Distributions
28*19c3b8c2SApple OSS Distributions static const int64_t kChunkSize = 100000000;
29*19c3b8c2SApple OSS Distributions
30*19c3b8c2SApple OSS Distributions /* Arguments parsed from the command line */
31*19c3b8c2SApple OSS Distributions typedef struct test_args {
32*19c3b8c2SApple OSS Distributions size_t n_threads;
33*19c3b8c2SApple OSS Distributions unsigned long long num_writes;
34*19c3b8c2SApple OSS Distributions test_variant_t variant;
35*19c3b8c2SApple OSS Distributions bool verbose;
36*19c3b8c2SApple OSS Distributions } test_args_t;
37*19c3b8c2SApple OSS Distributions
38*19c3b8c2SApple OSS Distributions typedef struct {
39*19c3b8c2SApple OSS Distributions char _padding1[128];
40*19c3b8c2SApple OSS Distributions atomic_bool tg_test_start;
41*19c3b8c2SApple OSS Distributions atomic_ullong tg_num_writes_remaining;
42*19c3b8c2SApple OSS Distributions atomic_ullong tg_threads_ready;
43*19c3b8c2SApple OSS Distributions test_args_t tg_args;
44*19c3b8c2SApple OSS Distributions uint64_t tg_start_time;
45*19c3b8c2SApple OSS Distributions uint64_t tg_end_time;
46*19c3b8c2SApple OSS Distributions uint64_t tg_start_value;
47*19c3b8c2SApple OSS Distributions uint64_t tg_end_value;
48*19c3b8c2SApple OSS Distributions char _padding2[128];
49*19c3b8c2SApple OSS Distributions } test_globals_t;
50*19c3b8c2SApple OSS Distributions
51*19c3b8c2SApple OSS Distributions static void parse_arguments(int argc, char** argv, test_args_t *args);
52*19c3b8c2SApple OSS Distributions static const char *get_sysctl_name_for_test_variant(test_variant_t variant);
53*19c3b8c2SApple OSS Distributions static void *writer(void *);
54*19c3b8c2SApple OSS Distributions static uint64_t counter_read(test_variant_t);
55*19c3b8c2SApple OSS Distributions
56*19c3b8c2SApple OSS Distributions int
main(int argc,char ** argv)57*19c3b8c2SApple OSS Distributions main(int argc, char** argv)
58*19c3b8c2SApple OSS Distributions {
59*19c3b8c2SApple OSS Distributions test_globals_t globals = {0};
60*19c3b8c2SApple OSS Distributions pthread_t* threads = NULL;
61*19c3b8c2SApple OSS Distributions int ret;
62*19c3b8c2SApple OSS Distributions int is_development_kernel;
63*19c3b8c2SApple OSS Distributions size_t is_development_kernel_size = sizeof(is_development_kernel);
64*19c3b8c2SApple OSS Distributions pthread_attr_t pthread_attrs;
65*19c3b8c2SApple OSS Distributions uint64_t duration, writes_stored;
66*19c3b8c2SApple OSS Distributions double writes_per_second;
67*19c3b8c2SApple OSS Distributions double loss;
68*19c3b8c2SApple OSS Distributions
69*19c3b8c2SApple OSS Distributions if (sysctlbyname("kern.development", &is_development_kernel,
70*19c3b8c2SApple OSS Distributions &is_development_kernel_size, NULL, 0) != 0 || !is_development_kernel) {
71*19c3b8c2SApple OSS Distributions fprintf(stderr, "%s requires the development kernel\n", argv[0]);
72*19c3b8c2SApple OSS Distributions exit(1);
73*19c3b8c2SApple OSS Distributions }
74*19c3b8c2SApple OSS Distributions
75*19c3b8c2SApple OSS Distributions parse_arguments(argc, argv, &(globals.tg_args));
76*19c3b8c2SApple OSS Distributions atomic_store(&(globals.tg_num_writes_remaining), globals.tg_args.num_writes);
77*19c3b8c2SApple OSS Distributions
78*19c3b8c2SApple OSS Distributions threads = malloc(sizeof(pthread_t) * globals.tg_args.n_threads);
79*19c3b8c2SApple OSS Distributions assert(threads);
80*19c3b8c2SApple OSS Distributions ret = pthread_attr_init(&pthread_attrs);
81*19c3b8c2SApple OSS Distributions assert(ret == 0);
82*19c3b8c2SApple OSS Distributions ret = init_scalable_counter_test();
83*19c3b8c2SApple OSS Distributions assert(ret == 0);
84*19c3b8c2SApple OSS Distributions globals.tg_start_value = counter_read(globals.tg_args.variant);
85*19c3b8c2SApple OSS Distributions for (size_t i = 0; i < globals.tg_args.n_threads; i++) {
86*19c3b8c2SApple OSS Distributions ret = pthread_create(threads + i, &pthread_attrs, writer, &globals);
87*19c3b8c2SApple OSS Distributions assert(ret == 0);
88*19c3b8c2SApple OSS Distributions }
89*19c3b8c2SApple OSS Distributions for (size_t i = 0; i < globals.tg_args.n_threads; i++) {
90*19c3b8c2SApple OSS Distributions ret = pthread_join(threads[i], NULL);
91*19c3b8c2SApple OSS Distributions assert(ret == 0);
92*19c3b8c2SApple OSS Distributions }
93*19c3b8c2SApple OSS Distributions ret = fini_scalable_counter_test();
94*19c3b8c2SApple OSS Distributions assert(ret == 0);
95*19c3b8c2SApple OSS Distributions globals.tg_end_value = counter_read(globals.tg_args.variant);
96*19c3b8c2SApple OSS Distributions
97*19c3b8c2SApple OSS Distributions duration = globals.tg_end_time - globals.tg_start_time;
98*19c3b8c2SApple OSS Distributions printf("-----Results-----\n");
99*19c3b8c2SApple OSS Distributions printf("rate,loss\n");
100*19c3b8c2SApple OSS Distributions writes_per_second = globals.tg_args.num_writes / ((double) duration / kNumNanosecondsInSecond);
101*19c3b8c2SApple OSS Distributions writes_stored = globals.tg_end_value - globals.tg_start_value;
102*19c3b8c2SApple OSS Distributions loss = (1.0 - ((double) writes_stored / globals.tg_args.num_writes)) * 100;
103*19c3b8c2SApple OSS Distributions printf("%.4f,%.4f\n", writes_per_second, loss);
104*19c3b8c2SApple OSS Distributions return 0;
105*19c3b8c2SApple OSS Distributions }
106*19c3b8c2SApple OSS Distributions
107*19c3b8c2SApple OSS Distributions static void *
writer(void * arg)108*19c3b8c2SApple OSS Distributions writer(void *arg)
109*19c3b8c2SApple OSS Distributions {
110*19c3b8c2SApple OSS Distributions int ret;
111*19c3b8c2SApple OSS Distributions const char* sysctl_name;
112*19c3b8c2SApple OSS Distributions test_globals_t *globals = arg;
113*19c3b8c2SApple OSS Distributions int64_t value = kChunkSize;
114*19c3b8c2SApple OSS Distributions //size_t size = sizeof(value);
115*19c3b8c2SApple OSS Distributions
116*19c3b8c2SApple OSS Distributions sysctl_name = get_sysctl_name_for_test_variant(globals->tg_args.variant);
117*19c3b8c2SApple OSS Distributions assert(sysctl_name != NULL);
118*19c3b8c2SApple OSS Distributions
119*19c3b8c2SApple OSS Distributions if (atomic_fetch_add(&(globals->tg_threads_ready), 1) == globals->tg_args.n_threads - 1) {
120*19c3b8c2SApple OSS Distributions globals->tg_start_time = current_timestamp_ns();
121*19c3b8c2SApple OSS Distributions atomic_store(&globals->tg_test_start, true);
122*19c3b8c2SApple OSS Distributions }
123*19c3b8c2SApple OSS Distributions while (!atomic_load(&(globals->tg_test_start))) {
124*19c3b8c2SApple OSS Distributions ;
125*19c3b8c2SApple OSS Distributions }
126*19c3b8c2SApple OSS Distributions
127*19c3b8c2SApple OSS Distributions while (true) {
128*19c3b8c2SApple OSS Distributions unsigned long long remaining = atomic_fetch_sub(&(globals->tg_num_writes_remaining), value);
129*19c3b8c2SApple OSS Distributions if (remaining < kChunkSize || remaining > globals->tg_args.num_writes) {
130*19c3b8c2SApple OSS Distributions break;
131*19c3b8c2SApple OSS Distributions }
132*19c3b8c2SApple OSS Distributions
133*19c3b8c2SApple OSS Distributions ret = sysctlbyname(sysctl_name, NULL, NULL, &value, sizeof(value));
134*19c3b8c2SApple OSS Distributions assert(ret == 0);
135*19c3b8c2SApple OSS Distributions if (remaining == kChunkSize || remaining - kChunkSize > remaining) {
136*19c3b8c2SApple OSS Distributions break;
137*19c3b8c2SApple OSS Distributions }
138*19c3b8c2SApple OSS Distributions }
139*19c3b8c2SApple OSS Distributions
140*19c3b8c2SApple OSS Distributions if (atomic_fetch_sub(&(globals->tg_threads_ready), 1) == 1) {
141*19c3b8c2SApple OSS Distributions globals->tg_end_time = current_timestamp_ns();
142*19c3b8c2SApple OSS Distributions }
143*19c3b8c2SApple OSS Distributions
144*19c3b8c2SApple OSS Distributions return NULL;
145*19c3b8c2SApple OSS Distributions }
146*19c3b8c2SApple OSS Distributions
147*19c3b8c2SApple OSS Distributions static const char*
get_sysctl_name_for_test_variant(test_variant_t variant)148*19c3b8c2SApple OSS Distributions get_sysctl_name_for_test_variant(test_variant_t variant)
149*19c3b8c2SApple OSS Distributions {
150*19c3b8c2SApple OSS Distributions switch (variant) {
151*19c3b8c2SApple OSS Distributions case VARIANT_SCALABLE_COUNTER:
152*19c3b8c2SApple OSS Distributions return "kern.scalable_counter_write_benchmark";
153*19c3b8c2SApple OSS Distributions case VARIANT_ATOMIC:
154*19c3b8c2SApple OSS Distributions return "kern.scalable_counter_atomic_counter_write_benchmark";
155*19c3b8c2SApple OSS Distributions case VARIANT_RACY:
156*19c3b8c2SApple OSS Distributions return "kern.scalable_counter_racy_counter_benchmark";
157*19c3b8c2SApple OSS Distributions default:
158*19c3b8c2SApple OSS Distributions return NULL;
159*19c3b8c2SApple OSS Distributions }
160*19c3b8c2SApple OSS Distributions }
161*19c3b8c2SApple OSS Distributions
162*19c3b8c2SApple OSS Distributions static const char*
get_sysctl_load_name_for_test_variant(test_variant_t variant)163*19c3b8c2SApple OSS Distributions get_sysctl_load_name_for_test_variant(test_variant_t variant)
164*19c3b8c2SApple OSS Distributions {
165*19c3b8c2SApple OSS Distributions switch (variant) {
166*19c3b8c2SApple OSS Distributions case VARIANT_SCALABLE_COUNTER:
167*19c3b8c2SApple OSS Distributions return "kern.scalable_counter_test_load";
168*19c3b8c2SApple OSS Distributions case VARIANT_ATOMIC:
169*19c3b8c2SApple OSS Distributions return "kern.scalable_counter_atomic_counter_load";
170*19c3b8c2SApple OSS Distributions case VARIANT_RACY:
171*19c3b8c2SApple OSS Distributions return "kern.scalable_counter_racy_counter_load";
172*19c3b8c2SApple OSS Distributions default:
173*19c3b8c2SApple OSS Distributions return NULL;
174*19c3b8c2SApple OSS Distributions }
175*19c3b8c2SApple OSS Distributions }
176*19c3b8c2SApple OSS Distributions
177*19c3b8c2SApple OSS Distributions static uint64_t
counter_read(test_variant_t variant)178*19c3b8c2SApple OSS Distributions counter_read(test_variant_t variant)
179*19c3b8c2SApple OSS Distributions {
180*19c3b8c2SApple OSS Distributions const char *sysctl_name = get_sysctl_load_name_for_test_variant(variant);
181*19c3b8c2SApple OSS Distributions int result;
182*19c3b8c2SApple OSS Distributions uint64_t value;
183*19c3b8c2SApple OSS Distributions size_t size = sizeof(value);
184*19c3b8c2SApple OSS Distributions result = sysctlbyname(sysctl_name, &value, &size, NULL, 0);
185*19c3b8c2SApple OSS Distributions assert(result == 0);
186*19c3b8c2SApple OSS Distributions return value;
187*19c3b8c2SApple OSS Distributions }
188*19c3b8c2SApple OSS Distributions
189*19c3b8c2SApple OSS Distributions static void
print_help(char ** argv)190*19c3b8c2SApple OSS Distributions print_help(char** argv)
191*19c3b8c2SApple OSS Distributions {
192*19c3b8c2SApple OSS Distributions fprintf(stderr, "%s: <test-variant> [-v] num_writes num_threads\n", argv[0]);
193*19c3b8c2SApple OSS Distributions fprintf(stderr, "\ntest variants:\n");
194*19c3b8c2SApple OSS Distributions fprintf(stderr, " %s Benchmark scalable counters.\n", kScalableCounterArgument);
195*19c3b8c2SApple OSS Distributions fprintf(stderr, " %s Benchmark single atomic counter.\n", kAtomicCounterArgument);
196*19c3b8c2SApple OSS Distributions fprintf(stderr, " %s Benchmark racy counter.\n", kRacyCounterArgument);
197*19c3b8c2SApple OSS Distributions }
198*19c3b8c2SApple OSS Distributions
199*19c3b8c2SApple OSS Distributions static void
parse_arguments(int argc,char ** argv,test_args_t * args)200*19c3b8c2SApple OSS Distributions parse_arguments(int argc, char** argv, test_args_t *args)
201*19c3b8c2SApple OSS Distributions {
202*19c3b8c2SApple OSS Distributions int current_argument = 1;
203*19c3b8c2SApple OSS Distributions memset(args, 0, sizeof(test_args_t));
204*19c3b8c2SApple OSS Distributions if (argc < 4 || argc > 6) {
205*19c3b8c2SApple OSS Distributions print_help(argv);
206*19c3b8c2SApple OSS Distributions exit(1);
207*19c3b8c2SApple OSS Distributions }
208*19c3b8c2SApple OSS Distributions if (argv[current_argument][0] == '-') {
209*19c3b8c2SApple OSS Distributions if (strcmp(argv[current_argument], "-v") == 0) {
210*19c3b8c2SApple OSS Distributions args->verbose = true;
211*19c3b8c2SApple OSS Distributions } else {
212*19c3b8c2SApple OSS Distributions fprintf(stderr, "Unknown argument %s\n", argv[current_argument]);
213*19c3b8c2SApple OSS Distributions print_help(argv);
214*19c3b8c2SApple OSS Distributions exit(1);
215*19c3b8c2SApple OSS Distributions }
216*19c3b8c2SApple OSS Distributions current_argument++;
217*19c3b8c2SApple OSS Distributions }
218*19c3b8c2SApple OSS Distributions if (strncasecmp(argv[current_argument], kScalableCounterArgument, strlen(kScalableCounterArgument)) == 0) {
219*19c3b8c2SApple OSS Distributions args->variant = VARIANT_SCALABLE_COUNTER;
220*19c3b8c2SApple OSS Distributions } else if (strncasecmp(argv[current_argument], kAtomicCounterArgument, strlen(kAtomicCounterArgument)) == 0) {
221*19c3b8c2SApple OSS Distributions args->variant = VARIANT_ATOMIC;
222*19c3b8c2SApple OSS Distributions } else if (strncasecmp(argv[current_argument], kRacyCounterArgument, strlen(kRacyCounterArgument)) == 0) {
223*19c3b8c2SApple OSS Distributions args->variant = VARIANT_RACY;
224*19c3b8c2SApple OSS Distributions } else {
225*19c3b8c2SApple OSS Distributions print_help(argv);
226*19c3b8c2SApple OSS Distributions exit(1);
227*19c3b8c2SApple OSS Distributions }
228*19c3b8c2SApple OSS Distributions current_argument++;
229*19c3b8c2SApple OSS Distributions
230*19c3b8c2SApple OSS Distributions long num_writes = strtol(argv[current_argument++], NULL, 10);
231*19c3b8c2SApple OSS Distributions if (num_writes == 0) {
232*19c3b8c2SApple OSS Distributions print_help(argv);
233*19c3b8c2SApple OSS Distributions exit(1);
234*19c3b8c2SApple OSS Distributions }
235*19c3b8c2SApple OSS Distributions long num_cores = strtol(argv[current_argument++], NULL, 10);
236*19c3b8c2SApple OSS Distributions if (num_cores == 0) {
237*19c3b8c2SApple OSS Distributions print_help(argv);
238*19c3b8c2SApple OSS Distributions exit(1);
239*19c3b8c2SApple OSS Distributions }
240*19c3b8c2SApple OSS Distributions assert(num_cores > 0 && num_cores <= get_ncpu());
241*19c3b8c2SApple OSS Distributions args->n_threads = (unsigned int) num_cores;
242*19c3b8c2SApple OSS Distributions args->num_writes = (unsigned long long) num_writes;
243*19c3b8c2SApple OSS Distributions }
244