/* * Copyright © 2012 Ran Benita * SPDX-License-Identifier: MIT */ #include "config.h" #include #include #include "xkbcommon/xkbcommon.h" #include "../test/test.h" #include "xkbcomp/rules.h" #include "bench.h" static const unsigned int DEFAULT_ITERATIONS = 20000; static const double DEFAULT_STDEV = 0.05; static void usage(char **argv) { printf("Usage: %s [OPTIONS]\n" "\n" "Benchmark compilation of the given RMLVO\n" "\n" "Options:\n" " --help\n" " Print this help and exit\n" " --iter\n" " Exact number of iterations to run\n" " --stdev\n" " Minimal relative standard deviation (percentage) to reach.\n" " (default: %f)\n" "Note: --iter and --stdev are mutually exclusive.\n" "\n" "XKB-specific options:\n" " --rules \n" " The XKB ruleset (default: '%s')\n" " --model \n" " The XKB model (default: '%s')\n" " --layout \n" " The XKB layout (default: '%s')\n" " --variant \n" " The XKB layout variant (default: '%s')\n" " --options \n" " The XKB options (default: '%s')\n" "\n", argv[0], DEFAULT_STDEV * 100, DEFAULT_XKB_RULES, DEFAULT_XKB_MODEL, DEFAULT_XKB_LAYOUT, DEFAULT_XKB_VARIANT ? DEFAULT_XKB_VARIANT : "", DEFAULT_XKB_OPTIONS ? DEFAULT_XKB_OPTIONS : ""); } int main(int argc, char *argv[]) { struct bench bench; struct bench_time elapsed; struct estimate est; bool explicit_iterations = false; struct xkb_rule_names rmlvo = { .rules = DEFAULT_XKB_RULES, .model = DEFAULT_XKB_MODEL, /* layout and variant are tied together, so we either get user-supplied for * both or default for both, see below */ .layout = NULL, .variant = NULL, .options = DEFAULT_XKB_OPTIONS, }; unsigned int max_iterations = DEFAULT_ITERATIONS; double stdev = DEFAULT_STDEV; enum options { OPT_RULES, OPT_MODEL, OPT_LAYOUT, OPT_VARIANT, OPT_OPTION, OPT_ITERATIONS, OPT_STDEV, }; static struct option opts[] = { {"help", no_argument, 0, 'h'}, {"rules", required_argument, 0, OPT_RULES}, {"model", required_argument, 0, OPT_MODEL}, {"layout", required_argument, 0, OPT_LAYOUT}, {"variant", required_argument, 0, OPT_VARIANT}, {"options", required_argument, 0, OPT_OPTION}, {"iter", required_argument, 0, OPT_ITERATIONS}, {"stdev", required_argument, 0, OPT_STDEV}, {0, 0, 0, 0}, }; while (1) { int c; int option_index = 0; c = getopt_long(argc, argv, "h", opts, &option_index); if (c == -1) break; switch (c) { case 'h': usage(argv); exit(EXIT_SUCCESS); case OPT_RULES: rmlvo.rules = optarg; break; case OPT_MODEL: rmlvo.model = optarg; break; case OPT_LAYOUT: rmlvo.layout = optarg; break; case OPT_VARIANT: rmlvo.variant = optarg; break; case OPT_OPTION: rmlvo.options = optarg; break; case OPT_ITERATIONS: if (max_iterations == 0) { usage(argv); exit(EXIT_INVALID_USAGE); } { errno = 0; char *endp = optarg; const unsigned long raw = strtoul(optarg, &endp, 10); if (errno || optarg == endp || *endp != '\0' || !raw || raw > UINT_MAX) { fprintf(stderr, "ERROR: invalid 'iter' parameter; " "using default: %u\n", DEFAULT_ITERATIONS); max_iterations = DEFAULT_ITERATIONS; } else { max_iterations = (unsigned int)raw; } } explicit_iterations = true; break; case OPT_STDEV: if (explicit_iterations) { usage(argv); exit(EXIT_INVALID_USAGE); } { errno = 0; char *endp = optarg; stdev = strtod(optarg, &endp) / 100; if (errno || optarg == endp || *endp != '\0' || stdev <= 0) { fprintf(stderr, "ERROR: invalid 'stdev' parameter; " "using default: %.3f\n", DEFAULT_STDEV); stdev = DEFAULT_STDEV; } } max_iterations = 0; break; default: usage(argv); exit(EXIT_INVALID_USAGE); } } /* Now fill in the layout */ if (!rmlvo.layout || !*rmlvo.layout) { if (rmlvo.variant && *rmlvo.variant) { fprintf(stderr, "Error: a variant requires a layout\n"); return EXIT_INVALID_USAGE; } rmlvo.layout = DEFAULT_XKB_LAYOUT; rmlvo.variant = DEFAULT_XKB_VARIANT; } struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) exit(EXIT_FAILURE); xkb_enable_quiet_logging(context); if (explicit_iterations) { stdev = 0; bench_start2(&bench); for (unsigned int i = 0; i < max_iterations; i++) { struct xkb_component_names kccgst; assert(xkb_components_from_rules_names(context, &rmlvo, &kccgst, NULL)); free(kccgst.keycodes); free(kccgst.types); free(kccgst.compatibility); free(kccgst.symbols); free(kccgst.geometry); } bench_stop2(&bench); bench_elapsed(&bench, &elapsed); est.elapsed = (bench_time_elapsed_nanoseconds(&elapsed)) / max_iterations; est.stdev = 0; } else { bench_start2(&bench); BENCH(stdev, max_iterations, elapsed, est, struct xkb_component_names kccgst; assert(xkb_components_from_rules_names(context, &rmlvo, &kccgst, NULL)); free(kccgst.keycodes); free(kccgst.types); free(kccgst.compatibility); free(kccgst.symbols); free(kccgst.geometry); ); bench_stop2(&bench); } struct bench_time total_elapsed; bench_elapsed(&bench, &total_elapsed); if (explicit_iterations) { fprintf(stderr, "mean: %lld µs; compiled %u rules in %ld.%06lds\n", est.elapsed / 1000, max_iterations, total_elapsed.seconds, total_elapsed.nanoseconds / 1000); } else { fprintf(stderr, "mean: %lld µs; stdev: %Lf%% (target: %f%%); " "last run: compiled %u rules in %ld.%06lds; " "total time: %ld.%06lds\n", est.elapsed / 1000, (long double) est.stdev * 100.0 / (long double) est.elapsed, stdev * 100, max_iterations, elapsed.seconds, elapsed.nanoseconds / 1000, total_elapsed.seconds, total_elapsed.nanoseconds / 1000); } xkb_context_unref(context); return EXIT_SUCCESS; }