1 /* Per-cpu counter microbenchmarks. */
12 #include <sys/types.h>
13 #include <sys/sysctl.h>
15 #include "benchmark/helpers.h"
16 #include "counter/common.h"
18 typedef enum test_variant
{
19 VARIANT_SCALABLE_COUNTER
,
24 static const char* kScalableCounterArgument
= "scalable";
25 static const char* kAtomicCounterArgument
= "atomic";
26 static const char* kRacyCounterArgument
= "racy";
28 static const int64_t kChunkSize
= 100000000;
30 /* Arguments parsed from the command line */
31 typedef struct test_args
{
33 unsigned long long num_writes
;
34 test_variant_t variant
;
40 atomic_bool tg_test_start
;
41 atomic_ullong tg_num_writes_remaining
;
42 atomic_ullong tg_threads_ready
;
44 uint64_t tg_start_time
;
46 uint64_t tg_start_value
;
47 uint64_t tg_end_value
;
51 static void parse_arguments(int argc
, char** argv
, test_args_t
*args
);
52 static const char *get_sysctl_name_for_test_variant(test_variant_t variant
);
53 static void *writer(void *);
54 static uint64_t counter_read(test_variant_t
);
57 main(int argc
, char** argv
)
59 test_globals_t globals
= {0};
60 pthread_t
* threads
= NULL
;
62 int is_development_kernel
;
63 size_t is_development_kernel_size
= sizeof(is_development_kernel
);
64 pthread_attr_t pthread_attrs
;
65 uint64_t duration
, writes_stored
;
66 double writes_per_second
;
69 if (sysctlbyname("kern.development", &is_development_kernel
,
70 &is_development_kernel_size
, NULL
, 0) != 0 || !is_development_kernel
) {
71 fprintf(stderr
, "%s requires the development kernel\n", argv
[0]);
75 parse_arguments(argc
, argv
, &(globals
.tg_args
));
76 atomic_store(&(globals
.tg_num_writes_remaining
), globals
.tg_args
.num_writes
);
78 threads
= malloc(sizeof(pthread_t
) * globals
.tg_args
.n_threads
);
80 ret
= pthread_attr_init(&pthread_attrs
);
82 ret
= init_scalable_counter_test();
84 globals
.tg_start_value
= counter_read(globals
.tg_args
.variant
);
85 for (size_t i
= 0; i
< globals
.tg_args
.n_threads
; i
++) {
86 ret
= pthread_create(threads
+ i
, &pthread_attrs
, writer
, &globals
);
89 for (size_t i
= 0; i
< globals
.tg_args
.n_threads
; i
++) {
90 ret
= pthread_join(threads
[i
], NULL
);
93 ret
= fini_scalable_counter_test();
95 globals
.tg_end_value
= counter_read(globals
.tg_args
.variant
);
97 duration
= globals
.tg_end_time
- globals
.tg_start_time
;
98 printf("-----Results-----\n");
99 printf("rate,loss\n");
100 writes_per_second
= globals
.tg_args
.num_writes
/ ((double) duration
/ kNumNanosecondsInSecond
);
101 writes_stored
= globals
.tg_end_value
- globals
.tg_start_value
;
102 loss
= (1.0 - ((double) writes_stored
/ globals
.tg_args
.num_writes
)) * 100;
103 printf("%.4f,%.4f\n", writes_per_second
, loss
);
111 const char* sysctl_name
;
112 test_globals_t
*globals
= arg
;
113 int64_t value
= kChunkSize
;
114 //size_t size = sizeof(value);
116 sysctl_name
= get_sysctl_name_for_test_variant(globals
->tg_args
.variant
);
117 assert(sysctl_name
!= NULL
);
119 if (atomic_fetch_add(&(globals
->tg_threads_ready
), 1) == globals
->tg_args
.n_threads
- 1) {
120 globals
->tg_start_time
= current_timestamp_ns();
121 atomic_store(&globals
->tg_test_start
, true);
123 while (!atomic_load(&(globals
->tg_test_start
))) {
128 unsigned long long remaining
= atomic_fetch_sub(&(globals
->tg_num_writes_remaining
), value
);
129 if (remaining
< kChunkSize
|| remaining
> globals
->tg_args
.num_writes
) {
133 ret
= sysctlbyname(sysctl_name
, NULL
, NULL
, &value
, sizeof(value
));
135 if (remaining
== kChunkSize
|| remaining
- kChunkSize
> remaining
) {
140 if (atomic_fetch_sub(&(globals
->tg_threads_ready
), 1) == 1) {
141 globals
->tg_end_time
= current_timestamp_ns();
148 get_sysctl_name_for_test_variant(test_variant_t variant
)
151 case VARIANT_SCALABLE_COUNTER
:
152 return "kern.scalable_counter_write_benchmark";
154 return "kern.scalable_counter_atomic_counter_write_benchmark";
156 return "kern.scalable_counter_racy_counter_benchmark";
163 get_sysctl_load_name_for_test_variant(test_variant_t variant
)
166 case VARIANT_SCALABLE_COUNTER
:
167 return "kern.scalable_counter_test_load";
169 return "kern.scalable_counter_atomic_counter_load";
171 return "kern.scalable_counter_racy_counter_load";
178 counter_read(test_variant_t variant
)
180 const char *sysctl_name
= get_sysctl_load_name_for_test_variant(variant
);
183 size_t size
= sizeof(value
);
184 result
= sysctlbyname(sysctl_name
, &value
, &size
, NULL
, 0);
190 print_help(char** argv
)
192 fprintf(stderr
, "%s: <test-variant> [-v] num_writes num_threads\n", argv
[0]);
193 fprintf(stderr
, "\ntest variants:\n");
194 fprintf(stderr
, " %s Benchmark scalable counters.\n", kScalableCounterArgument
);
195 fprintf(stderr
, " %s Benchmark single atomic counter.\n", kAtomicCounterArgument
);
196 fprintf(stderr
, " %s Benchmark racy counter.\n", kRacyCounterArgument
);
200 parse_arguments(int argc
, char** argv
, test_args_t
*args
)
202 int current_argument
= 1;
203 memset(args
, 0, sizeof(test_args_t
));
204 if (argc
< 4 || argc
> 6) {
208 if (argv
[current_argument
][0] == '-') {
209 if (strcmp(argv
[current_argument
], "-v") == 0) {
210 args
->verbose
= true;
212 fprintf(stderr
, "Unknown argument %s\n", argv
[current_argument
]);
218 if (strncasecmp(argv
[current_argument
], kScalableCounterArgument
, strlen(kScalableCounterArgument
)) == 0) {
219 args
->variant
= VARIANT_SCALABLE_COUNTER
;
220 } else if (strncasecmp(argv
[current_argument
], kAtomicCounterArgument
, strlen(kAtomicCounterArgument
)) == 0) {
221 args
->variant
= VARIANT_ATOMIC
;
222 } else if (strncasecmp(argv
[current_argument
], kRacyCounterArgument
, strlen(kRacyCounterArgument
)) == 0) {
223 args
->variant
= VARIANT_RACY
;
230 long num_writes
= strtol(argv
[current_argument
++], NULL
, 10);
231 if (num_writes
== 0) {
235 long num_cores
= strtol(argv
[current_argument
++], NULL
, 10);
236 if (num_cores
== 0) {
240 assert(num_cores
> 0 && num_cores
<= get_ncpu());
241 args
->n_threads
= (unsigned int) num_cores
;
242 args
->num_writes
= (unsigned long long) num_writes
;