#include #include // timespec #include // timeval enum { TIMEGRAN = 1000000000LL }; // nanosecond granularity, except for timeval #include volatile bool stop = false; bool clock_mode; double duration = -1; unsigned long long stop_count = 0; unsigned nprocs = 1; unsigned nthreads = 1; volatile unsigned long long threads_left; #define BENCH_OPT \ {'d', "duration", "Duration of the experiments in seconds", duration }, \ {'i', "iterations", "Number of iterations of the experiments", stop_count }, \ {'t', "nthreads", "Number of threads to use", nthreads }, \ {'p', "nprocs", "Number of processors to use", nprocs } #define BENCH_OPT_PARSE(name) \ { \ int opt_cnt = sizeof(opt) / sizeof(option_t); \ char **left; \ parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]...\n" name, &left ); \ if(duration > 0 && stop_count > 0) { \ fprintf(stderr, "--duration and --iterations cannot be used together\n"); \ print_args_usage(argc, argv, opt, opt_cnt, "[OPTIONS]...\n" name, true); \ } else if(duration > 0) { \ clock_mode = true; \ stop_count = 0xFFFFFFFFFFFFFFFF; \ printf("Running for %lf seconds\n", duration); \ } else if(stop_count > 0) { \ clock_mode = false; \ printf("Running for %llu iterations\n", stop_count); \ } else { \ duration = 5; clock_mode = true;\ printf("Running for %lf seconds\n", duration); \ } \ } uint64_t getTimeNsec() { timespec curr; clock_gettime( CLOCK_REALTIME, &curr ); return (int64_t)curr.tv_sec * TIMEGRAN + curr.tv_nsec; } uint64_t to_miliseconds( uint64_t durtn ) { return durtn / (TIMEGRAN / 1000LL); } double to_fseconds(uint64_t durtn ) { return durtn / (double)TIMEGRAN; } uint64_t from_fseconds(double sec) { return sec * TIMEGRAN; } void wait(const uint64_t & start, bool is_tty) { for(;;) { Fibre::usleep(100000); uint64_t end = getTimeNsec(); uint64_t delta = end - start; if(is_tty) { printf(" %.1f\r", to_fseconds(delta)); fflush(stdout); } if( clock_mode && delta >= from_fseconds(duration) ) { break; } else if( !clock_mode && threads_left == 0 ) { break; } } } class __attribute__((aligned(128))) bench_sem { Fibre * volatile ptr = nullptr; public: inline bool wait() { static Fibre * const ready = reinterpret_cast(1ull); for(;;) { Fibre * expected = this->ptr; if(expected == ready) { if(__atomic_compare_exchange_n(&this->ptr, &expected, nullptr, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { return false; } } else { /* paranoid */ assert( expected == nullptr ); if(__atomic_compare_exchange_n(&this->ptr, &expected, fibre_self(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { fibre_park(); return true; } } } } inline bool post() { static Fibre * const ready = reinterpret_cast(1ull); for(;;) { Fibre * expected = this->ptr; if(expected == ready) return false; if(expected == nullptr) { if(__atomic_compare_exchange_n(&this->ptr, &expected, ready, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { return false; } } else { if(__atomic_compare_exchange_n(&this->ptr, &expected, nullptr, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { fibre_unpark( expected ); return true; } } } } }; // ========================================================================================== #include //----------------------------------------------------------------------------- // Typed argument parsing bool parse_yesno(const char * arg, bool & value ) { if(strcmp(arg, "yes") == 0) { value = true; return true; } if(strcmp(arg, "no") == 0) { value = false; return true; } return false; } bool parse_settrue (const char *, bool & value ) { value = true; return true; } bool parse_setfalse(const char *, bool & value ) { value = false; return true; } bool parse(const char * arg, const char * & value ) { value = arg; return true; } bool parse(const char * arg, int & value) { char * end; int r = strtoll(arg, &end, 10); if(*end != '\0') return false; value = r; return true; } bool parse(const char * arg, unsigned & value) { char * end; unsigned long long int r = strtoull(arg, &end, 10); if(*end != '\0') return false; if(r > UINT_MAX) return false; value = r; return true; } bool parse(const char * arg, unsigned long & value) { char * end; unsigned long long int r = strtoull(arg, &end, 10); if(*end != '\0') return false; if(r > ULONG_MAX) return false; value = r; return true; } bool parse(const char * arg, unsigned long long & value) { char * end; unsigned long long int r = strtoull(arg, &end, 10); if(*end != '\0') return false; if(r > ULLONG_MAX) return false; value = r; return true; } bool parse(const char * arg, double & value) { char * end; double r = strtod(arg, &end); if(*end != '\0') return false; value = r; return true; } //----------------------------------------------------------------------------- struct option_t { char short_name; const char * long_name; const char * help; void * variable; bool (*parse_fun)(const char *, void * ); template inline option_t( char short_name, const char * long_name, const char * help, T & variable ) { this->short_name = short_name; this->long_name = long_name; this->help = help; this->variable = reinterpret_cast(&variable); this->parse_fun = reinterpret_cast(static_cast(parse)); } template inline option_t( char short_name, const char * long_name, const char * help, T & variable, bool (*parse)(const char *, T & )) { this->short_name = short_name; this->long_name = long_name; this->help = help; this->variable = reinterpret_cast(&variable); this->parse_fun = reinterpret_cast(parse); } }; extern option_t last_option; //----------------------------------------------------------------------------- #include #include #include #include extern "C" { #include #include extern FILE * stderr; extern FILE * stdout; extern int fileno(FILE *stream); extern int fprintf ( FILE * stream, const char * format, ... ); extern long long int strtoll (const char* str, char** endptr, int base); extern unsigned long long int strtoull(const char* str, char** endptr, int base); extern double strtod (const char* str, char** endptr); } static void usage(char * cmd, option_t options[], size_t opt_count, const char * usage, FILE * out) __attribute__ ((noreturn)); //----------------------------------------------------------------------------- // getopt_long wrapping void parse_args( int argc, char * argv[], option_t options[], size_t opt_count, const char * usage_msg, char ** * left ) { struct option optarr[opt_count + 2]; { int idx = 0; for(int i = 0; i < opt_count; i++) { if(options[i].long_name) { optarr[idx].name = options[i].long_name; optarr[idx].flag = nullptr; optarr[idx].val = options[i].short_name; if( ((intptr_t)options[i].parse_fun) == ((intptr_t)parse_settrue) || ((intptr_t)options[i].parse_fun) == ((intptr_t)parse_setfalse) ) { optarr[idx].has_arg = no_argument; } else { optarr[idx].has_arg = required_argument; } idx++; } } optarr[idx+0].name = "help"; optarr[idx+0].has_arg = no_argument; optarr[idx+0].flag = 0; optarr[idx+0].val = 'h'; optarr[idx+1].name = 0; optarr[idx+1].has_arg = no_argument; optarr[idx+1].flag = 0; optarr[idx+1].val = 0; } char optstring[opt_count * 3]; for(auto & o : optstring) { o = '\0'; } { int idx = 0; for(int i = 0; i < opt_count; i++) { optstring[idx] = options[i].short_name; idx++; if( ((intptr_t)options[i].parse_fun) != ((intptr_t)parse_settrue) && ((intptr_t)options[i].parse_fun) != ((intptr_t)parse_setfalse) ) { optstring[idx] = ':'; idx++; } } optstring[idx+0] = 'h'; optstring[idx+1] = '\0'; } FILE * out = stderr; for(;;) { int idx = 0; int opt = getopt_long(argc, argv, optstring, optarr, &idx); switch(opt) { case -1: if(left != nullptr) *left = argv + optind; return; case 'h': out = stdout; case '?': usage(argv[0], options, opt_count, usage_msg, out); default: for(int i = 0; i < opt_count; i++) { if(opt == options[i].short_name) { const char * arg = optarg ? optarg : ""; bool success = options[i].parse_fun( arg, options[i].variable ); if(success) goto NEXT_ARG; fprintf(out, "Argument '%s' for option %c could not be parsed\n\n", arg, (char)opt); usage(argv[0], options, opt_count, usage_msg, out); } } std::abort(); } NEXT_ARG:; } } //----------------------------------------------------------------------------- // Print usage static void printopt(FILE * out, int width, int max, char sn, const char * ln, const char * help) { int hwidth = max - (11 + width); if(hwidth <= 0) hwidth = max; fprintf(out, " -%c, --%-*s %.*s\n", sn, width, ln, hwidth, help); for(;;) { help += std::min(strlen(help), (unsigned long)hwidth); if('\0' == *help) break; fprintf(out, "%*s%.*s\n", width + 11, "", hwidth, help); } } __attribute__((noreturn)) void print_args_usage(int , char * argv[], option_t options[], size_t opt_count, const char * usage_msg, bool error) { usage(argv[0], options, opt_count, usage_msg, error ? stderr : stdout); } static __attribute__((noreturn)) void usage(char * cmd, option_t options[], size_t opt_count, const char * help, FILE * out) { int width = 0; { for(int i = 0; i < opt_count; i++) { if(options[i].long_name) { int w = strlen(options[i].long_name); if(w > width) width = w; } } } int max_width = 1000000; int outfd = fileno(out); if(isatty(outfd)) { struct winsize size; int ret = ioctl(outfd, TIOCGWINSZ, &size); if(ret < 0) abort(); // "ioctl error: (%d) %s\n", (int)errno, strerror(errno) max_width = size.ws_col; } fprintf(out, "Usage:\n %s %s\n", cmd, help); for(int i = 0; i < opt_count; i++) { printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help); } fprintf(out, " -%c, --%-*s %s\n", 'h', width, "help", "print this help message"); exit(out == stdout ? 0 : 1); }