source: benchmark/readyQ/rq_bench.hpp @ caa3e2c

Last change on this file since caa3e2c was 8faa6612, checked in by Thierry Delisle <tdelisle@…>, 3 years ago

Minor fixes to how benchmarks handle yes/no arguments

  • Property mode set to 100644
File size: 11.4 KB
Line 
1#include <cassert>
2#include <climits>
3#include <cstdint>
4#include <cstdio>
5
6#include <time.h>                                                                               // timespec
7#include <sys/time.h>                                                                   // timeval
8
9typedef __uint128_t __lehmer64_state_t;
10static inline uint64_t __lehmer64( __lehmer64_state_t & state ) {
11        state *= 0xda942042e4dd58b5;
12        return state >> 64;
13}
14
15enum { TIMEGRAN = 1000000000LL };                                       // nanosecond granularity, except for timeval
16
17
18volatile bool stop = false;
19bool clock_mode;
20double duration = -1;
21unsigned long long stop_count = 0;
22unsigned nprocs = 1;
23unsigned nthreads = 1;
24
25volatile unsigned long long threads_left;
26
27#define BENCH_OPT \
28        {'d', "duration",  "Duration of the experiments in seconds", duration }, \
29        {'i', "iterations",  "Number of iterations of the experiments", stop_count }, \
30        {'t', "nthreads",  "Number of threads to use", nthreads }, \
31        {'p', "nprocs",    "Number of processors to use", nprocs }
32
33#define BENCH_OPT_PARSE(name) \
34        { \
35                int opt_cnt = sizeof(opt) / sizeof(option_t); \
36                char **left; \
37                parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]...\n" name, &left ); \
38                if(duration > 0 && stop_count > 0) { \
39                        fprintf(stderr, "--duration and --iterations cannot be used together\n"); \
40                        print_args_usage(argc, argv, opt, opt_cnt, "[OPTIONS]...\n" name, true); \
41                } else if(duration > 0) { \
42                        clock_mode = true; \
43                        stop_count = 0xFFFFFFFFFFFFFFFF; \
44                        printf("Running for %lf seconds\n", duration); \
45                } else if(stop_count > 0) { \
46                        clock_mode = false; \
47                        printf("Running for %llu iterations\n", stop_count); \
48                } else { \
49                        duration = 5; clock_mode = true;\
50                        printf("Running for %lf seconds\n", duration); \
51                } \
52        }
53
54uint64_t timeHiRes() {
55        timespec curr;
56        clock_gettime( CLOCK_REALTIME, &curr );
57        return (int64_t)curr.tv_sec * TIMEGRAN + curr.tv_nsec;
58}
59
60uint64_t to_miliseconds( uint64_t durtn ) { return durtn / (TIMEGRAN / 1000LL); }
61double to_fseconds(uint64_t durtn ) { return durtn / (double)TIMEGRAN; }
62uint64_t from_fseconds(double sec) { return sec * TIMEGRAN; }
63
64template<typename Sleeper>
65void wait(const uint64_t & start, bool is_tty) {
66        for(;;) {
67                Sleeper::usleep(100000);
68                uint64_t end = timeHiRes();
69                uint64_t delta = end - start;
70                if(is_tty) {
71                        printf(" %.1f\r", to_fseconds(delta));
72                        fflush(stdout);
73                }
74                if( clock_mode && delta >= from_fseconds(duration) ) {
75                        break;
76                }
77                else if( !clock_mode && threads_left == 0 ) {
78                        break;
79                }
80        }
81}
82
83class Fibre;
84int fibre_park();
85int fibre_unpark( Fibre * );
86Fibre * fibre_self();
87
88class __attribute__((aligned(128))) bench_sem {
89        Fibre * volatile ptr = nullptr;
90public:
91        inline bool wait() {
92                static Fibre * const ready  = reinterpret_cast<Fibre *>(1ull);
93                for(;;) {
94                        Fibre * expected = this->ptr;
95                        if(expected == ready) {
96                                if(__atomic_compare_exchange_n(&this->ptr, &expected, nullptr, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
97                                        return false;
98                                }
99                        }
100                        else {
101                                /* paranoid */ assert( expected == nullptr );
102                                if(__atomic_compare_exchange_n(&this->ptr, &expected, fibre_self(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
103                                        fibre_park();
104                                        return true;
105                                }
106                        }
107
108                }
109        }
110
111        inline bool post() {
112                static Fibre * const ready  = reinterpret_cast<Fibre *>(1ull);
113                for(;;) {
114                        Fibre * expected = this->ptr;
115                        if(expected == ready) return false;
116                        if(expected == nullptr) {
117                                if(__atomic_compare_exchange_n(&this->ptr, &expected, ready, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
118                                        return false;
119                                }
120                        }
121                        else {
122                                if(__atomic_compare_exchange_n(&this->ptr, &expected, nullptr, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
123                                        fibre_unpark( expected );
124                                        return true;
125                                }
126                        }
127                }
128        }
129};
130
131// ==========================================================================================
132#include <cstdlib>
133#include <cstring>
134
135#include <algorithm>
136
137//-----------------------------------------------------------------------------
138// Typed argument parsing
139bool parse_yesno(const char * arg, bool & value ) {
140        if(strcmp(arg, "yes") == 0) {
141                value = true;
142                return true;
143        }
144
145        if(strcmp(arg, "Y") == 0) {
146                value = true;
147                return true;
148        }
149
150        if(strcmp(arg, "y") == 0) {
151                value = true;
152                return true;
153        }
154
155        if(strcmp(arg, "no") == 0) {
156                value = false;
157                return true;
158        }
159
160        if(strcmp(arg, "N") == 0) {
161                value = false;
162                return true;
163        }
164
165        if(strcmp(arg, "n") == 0) {
166                value = false;
167                return true;
168        }
169
170        return false;
171}
172
173bool parse_truefalse(const char * arg, bool & value) {
174        if(strcmp(arg, "true") == 0) {
175                value = true;
176                return true;
177        }
178
179        if(strcmp(arg, "false") == 0) {
180                value = false;
181                return true;
182        }
183
184        return false;
185}
186
187bool parse_settrue (const char *, bool & value ) {
188        value = true;
189        return true;
190}
191
192bool parse_setfalse(const char *, bool & value )  {
193        value = false;
194        return true;
195}
196
197bool parse(const char * arg, const char * & value ) {
198        value = arg;
199        return true;
200}
201
202bool parse(const char * arg, int & value) {
203        char * end;
204        int r = strtoll(arg, &end, 10);
205        if(*end != '\0') return false;
206
207        value = r;
208        return true;
209}
210
211bool parse(const char * arg, unsigned & value) {
212        char * end;
213        unsigned long long int r = strtoull(arg, &end, 10);
214        if(*end != '\0') return false;
215        if(r > UINT_MAX) return false;
216
217        value = r;
218        return true;
219}
220
221bool parse(const char * arg, unsigned long & value) {
222        char * end;
223        unsigned long long int r = strtoull(arg, &end, 10);
224        if(*end != '\0') return false;
225        if(r > ULONG_MAX) return false;
226
227        value = r;
228        return true;
229}
230
231bool parse(const char * arg, unsigned long long & value) {
232        char * end;
233        unsigned long long int r = strtoull(arg, &end, 10);
234        if(*end != '\0') return false;
235        if(r > ULLONG_MAX) return false;
236
237        value = r;
238        return true;
239}
240
241bool parse(const char * arg, double & value) {
242        char * end;
243        double r = strtod(arg, &end);
244        if(*end != '\0') return false;
245
246        value = r;
247        return true;
248}
249
250//-----------------------------------------------------------------------------
251struct option_t {
252      char short_name;
253      const char * long_name;
254      const char * help;
255      void * variable;
256      bool (*parse_fun)(const char *, void * );
257
258        template<typename T>
259        inline option_t( char short_name, const char * long_name, const char * help, T & variable ) {
260                this->short_name = short_name;
261                this->long_name  = long_name;
262                this->help       = help;
263                this->variable   = reinterpret_cast<void*>(&variable);
264                #pragma GCC diagnostic push
265                #pragma GCC diagnostic ignored "-Wcast-function-type"
266                                this->parse_fun  = reinterpret_cast<bool (*)(const char *, void * )>(static_cast<bool (*)(const char *, T & )>(parse));
267                #pragma GCC diagnostic pop
268        }
269
270        template<typename T>
271        inline option_t( char short_name, const char * long_name, const char * help, T & variable, bool (*parse)(const char *, T & )) {
272                this->short_name = short_name;
273                this->long_name  = long_name;
274                this->help       = help;
275                this->variable   = reinterpret_cast<void*>(&variable);
276                #pragma GCC diagnostic push
277                #pragma GCC diagnostic ignored "-Wcast-function-type"
278                        this->parse_fun  = reinterpret_cast<bool (*)(const char *, void * )>(parse);
279                #pragma GCC diagnostic pop
280        }
281};
282
283extern option_t last_option;
284
285
286//-----------------------------------------------------------------------------
287#include <cstdint>
288#include <climits>
289#include <errno.h>
290#include <unistd.h>
291extern "C" {
292        #include <getopt.h>
293        #include <sys/ioctl.h>
294
295        extern FILE * stderr;
296        extern FILE * stdout;
297
298        extern int fileno(FILE *stream);
299
300        extern int fprintf ( FILE * stream, const char * format, ... );
301
302        extern          long long int strtoll (const char* str, char** endptr, int base);
303        extern unsigned long long int strtoull(const char* str, char** endptr, int base);
304        extern                 double strtod  (const char* str, char** endptr);
305}
306
307static void usage(char * cmd, option_t options[], size_t opt_count, const char * usage, FILE * out)  __attribute__ ((noreturn));
308
309//-----------------------------------------------------------------------------
310// getopt_long wrapping
311void parse_args(
312        int argc,
313        char * argv[],
314        option_t options[],
315        size_t opt_count,
316        const char * usage_msg,
317        char ** * left
318) {
319        struct option optarr[opt_count + 2];
320        {
321                int idx = 0;
322                for(size_t i = 0; i < opt_count; i++) {
323                        if(options[i].long_name) {
324                                optarr[idx].name = options[i].long_name;
325                                optarr[idx].flag = nullptr;
326                                optarr[idx].val  = options[i].short_name;
327                                if(    ((intptr_t)options[i].parse_fun) == ((intptr_t)parse_settrue)
328                                    || ((intptr_t)options[i].parse_fun) == ((intptr_t)parse_setfalse) ) {
329                                        optarr[idx].has_arg = no_argument;
330                                } else {
331                                        optarr[idx].has_arg = required_argument;
332                                }
333                                idx++;
334                        }
335                }
336                optarr[idx+0].name = "help";
337                optarr[idx+0].has_arg = no_argument;
338                optarr[idx+0].flag = 0;
339                optarr[idx+0].val = 'h';
340                optarr[idx+1].name = 0;
341                optarr[idx+1].has_arg = no_argument;
342                optarr[idx+1].flag = 0;
343                optarr[idx+1].val = 0;
344        }
345
346        char optstring[opt_count * 3];
347        for(auto & o : optstring) {
348                o = '\0';
349        }
350        {
351                int idx = 0;
352                for(size_t i = 0; i < opt_count; i++) {
353                        optstring[idx] = options[i].short_name;
354                        idx++;
355                        if(    ((intptr_t)options[i].parse_fun) != ((intptr_t)parse_settrue)
356                            && ((intptr_t)options[i].parse_fun) != ((intptr_t)parse_setfalse) ) {
357                                optstring[idx] = ':';
358                                idx++;
359                        }
360                }
361                optstring[idx+0] = 'h';
362                optstring[idx+1] = '\0';
363        }
364
365        FILE * out = stderr;
366        for(;;) {
367                int idx = 0;
368                int opt = getopt_long(argc, argv, optstring, optarr, &idx);
369                switch(opt) {
370                        case -1:
371                                if(left != nullptr) *left = argv + optind;
372                                return;
373                        case 'h':
374                                out = stdout;
375                                [[fallthrough]];
376                        case '?':
377                                usage(argv[0], options, opt_count, usage_msg, out);
378                        default:
379                                for(size_t i = 0; i < opt_count; i++) {
380                                        if(opt == options[i].short_name) {
381                                                const char * arg = optarg ? optarg : "";
382                                                if( arg[0] == '=' ) { arg++; }
383                                                bool success = options[i].parse_fun( arg, options[i].variable );
384                                                if(success) goto NEXT_ARG;
385
386                                                fprintf(out, "Argument '%s' for option %c could not be parsed\n\n", arg, (char)opt);
387                                                usage(argv[0], options, opt_count, usage_msg, out);
388                                        }
389                                }
390                                std::abort();
391                }
392                NEXT_ARG:;
393        }
394}
395
396//-----------------------------------------------------------------------------
397// Print usage
398static void printopt(FILE * out, int width, int max, char sn, const char * ln, const char * help) {
399        int hwidth = max - (11 + width);
400        if(hwidth <= 0) hwidth = max;
401
402        fprintf(out, "  -%c, --%-*s   %.*s\n", sn, width, ln, hwidth, help);
403        for(;;) {
404                help += std::min(strlen(help), (unsigned long)hwidth);
405                if('\0' == *help) break;
406                fprintf(out, "%*s%.*s\n", width + 11, "", hwidth, help);
407        }
408}
409
410__attribute__((noreturn)) void print_args_usage(int , char * argv[], option_t options[], size_t opt_count, const char * usage_msg, bool error) {
411        usage(argv[0], options, opt_count, usage_msg, error ? stderr : stdout);
412}
413
414static __attribute__((noreturn)) void usage(char * cmd, option_t options[], size_t opt_count, const char * help, FILE * out) {
415        int width = 0;
416        {
417                for(size_t i = 0; i < opt_count; i++) {
418                        if(options[i].long_name) {
419                                int w = strlen(options[i].long_name);
420                                if(w > width) width = w;
421                        }
422                }
423        }
424
425        int max_width = 1000000;
426        int outfd = fileno(out);
427        if(isatty(outfd)) {
428                struct winsize size;
429                int ret = ioctl(outfd, TIOCGWINSZ, &size);
430                if(ret < 0) abort(); // "ioctl error: (%d) %s\n", (int)errno, strerror(errno)
431                max_width = size.ws_col;
432        }
433
434        fprintf(out, "Usage:\n  %s %s\n", cmd, help);
435
436        for(size_t i = 0; i < opt_count; i++) {
437                printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help);
438        }
439        fprintf(out, "  -%c, --%-*s   %s\n", 'h', width, "help", "print this help message");
440        exit(out == stdout ? 0 : 1);
441}
442
Note: See TracBrowser for help on using the repository browser.