X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/c91413e6acbc8d157ff52ceb8cd78cee97403584..c81c35dfd10050ffef85d57dc2ad73f52f38a3f2:/test/tvec-bench.c diff --git a/test/tvec-bench.c b/test/tvec-bench.c index 8c8cfdf..c1d8cc0 100644 --- a/test/tvec-bench.c +++ b/test/tvec-bench.c @@ -81,6 +81,110 @@ static void normalize(double *x_inout, const char **unit_out, double scale) *x_inout = x; *unit_out = *u; } +/* --- @benchloop_...@ --- * + * + * Arguments: @unsigned long n@ = iteration count + * @void *ctx@ = benchmark running context + * + * Returns: --- + * + * Use: Run various kinds of benchmarking loops. + * + * * The @..._outer_...@ functions call the underlying + * function @n@ times in a loop; by contrast, the + * @..._inner_...@ functions set a register value to the + * chosen iteration count and expect the underlying function + * to perform the loop itself. + * + * * The @..._direct@ functions just call the underlying test + * function directly (though still through an `indirect + * jump' instruction); by contrast, the @..._indirect@ + * functions invoke a subsidiary environment's @run@ + * function, which adds additional overhead. + */ + +static void benchloop_outer_direct(unsigned long n, void *ctx) +{ + struct benchrun *r = ctx; + tvec_testfn *fn = r->fn; void *tctx = r->ctx; + const struct tvec_reg *in = r->in; struct tvec_reg *out = r->out; + + while (n--) fn(in, out, tctx); +} + +static void benchloop_inner_direct(unsigned long n, void *ctx) + { struct benchrun *r = ctx; *r->n = n; r->fn(r->in, r->out, r->ctx); } + +static void benchloop_outer_indirect(unsigned long n, void *ctx) +{ + struct benchrun *r = ctx; + struct tvec_state *tv = r->tv; + void (*run)(struct tvec_state *, tvec_testfn, void *) = r->env->run; + tvec_testfn *fn = r->fn; void *tctx = r->ctx; + + while (n--) run(tv, fn, tctx); +} + +static void benchloop_inner_indirect(unsigned long n, void *ctx) + { struct benchrun *r = ctx; *r->n = n; r->env->run(r->tv, r->fn, r->ctx); } + +/*----- Output utilities --------------------------------------------------*/ + +/* --- @tvec_benchreport@ --- * + * + * Arguments: @const struct gprintf_ops *gops@ = print operations + * @void *go@ = print destination + * @unsigned unit@ = the unit being measured (~TVBU_...@) + * @const struct bench_timing *tm@ = the benchmark result + * + * Returns: --- + * + * Use: Formats a report about the benchmark performance. This + * function is intended to be called on by an output + * @ebench@ function. + */ + +void tvec_benchreport(const struct gprintf_ops *gops, void *go, + unsigned unit, const struct bench_timing *tm) +{ + double scale, x, n = tm->n; + const char *u, *what, *whats; + + if (!tm) { gprintf(gops, go, "benchmark FAILED"); return; } + + assert(tm->f&BTF_TIMEOK); + + switch (unit) { + case TVBU_OP: + gprintf(gops, go, "%.0f iterations ", n); + what = "op"; whats = "ops"; scale = 1000; + break; + case TVBU_BYTE: + x = n; normalize(&x, &u, 1024); gprintf(gops, go, "%.3f %sB ", x, u); + what = whats = "B"; scale = 1024; + break; + default: + abort(); + } + + x = tm->t; normalize(&x, &u, 1000); + gprintf(gops, go, "in %.3f %ss", x, u); + if (tm->f&BTF_CYOK) { + x = tm->cy; normalize(&x, &u, 1000); + gprintf(gops, go, " (%.3f %scy)", x, u); + } + gprintf(gops, go, ": "); + + x = n/tm->t; normalize(&x, &u, scale); + gprintf(gops, go, "%.3f %s%s/s", x, u, whats); + x = tm->t/n; normalize(&x, &u, 1000); + gprintf(gops, go, ", %.3f %ss/%s", x, u, what); + if (tm->f&BTF_CYOK) { + x = tm->cy/n; normalize(&x, &u, 1000); + gprintf(gops, go, " (%.3f %scy/%s)", x, u, what); + } +} + /*----- Benchmark environment scaffolding ---------------------------------*/ /* --- @tvec_benchsetup@ --- * @@ -143,10 +247,10 @@ fail_timer: * * Arguments: @struct tvec_state *tv@ = test vector state * @const char *var@ = variable name to set - * @const struct tvec_env *env@ = environment description * @void *ctx@ = context pointer * - * Returns: Zero on success, @-1@ on failure. + * Returns: %$+1$% on success, %$0$% if the variable name was not + * recognized, or %$-1$% on any other error. * * Use: Set a special variable. The following special variables are * supported. @@ -158,11 +262,10 @@ fail_timer: * environment, if there is one. */ -int tvec_benchset(struct tvec_state *tv, const char *var, - const struct tvec_env *env, void *ctx) +int tvec_benchset(struct tvec_state *tv, const char *var, void *ctx) { struct tvec_benchctx *bc = ctx; - const struct tvec_benchenv *be = (const struct tvec_benchenv *)env; + const struct tvec_benchenv *be = bc->be; const struct tvec_env *subenv = be->env; union tvec_regval rv; static const struct tvec_floatinfo fi = { TVFF_NOMAX, 0.0, 0.0, 0.0 }; @@ -170,11 +273,11 @@ int tvec_benchset(struct tvec_state *tv, const char *var, { "@target", -1, &tvty_float, 0, { &fi } }; if (STRCMP(var, ==, "@target")) { + if (bc->f&TVBF_SETTRG) return (tvec_dupreg(tv, var)); if (tvty_float.parse(&rv, &rd, tv)) return (-1); - if (bc) bc->bst->target_s = rv.f; - return (1); + bc->bst->target_s = rv.f; bc->f |= TVBF_SETTRG; return (1); } else if (subenv && subenv->set) - return (subenv->set(tv, var, subenv, bc->subctx)); + return (subenv->set(tv, var, bc->subctx)); else return (0); } @@ -200,109 +303,6 @@ void tvec_benchbefore(struct tvec_state *tv, void *ctx) if (subenv && subenv->before) subenv->before(tv, bc->subctx); } -/* --- @tvec_benchafter@ --- * - * - * Arguments: @struct tvec_state *tv@ = test vector state - * @void *ctx@ = context pointer - * - * Returns: --- - * - * Use: Invoke the subordinate environment's @after@ function to - * clean up after the benchmark. - */ - -void tvec_benchafter(struct tvec_state *tv, void *ctx) -{ - struct tvec_benchctx *bc = ctx; - const struct tvec_benchenv *be = bc->be; - const struct tvec_env *subenv = be->env; - - /* Restore the benchmark state's old target. */ - bc->bst->target_s = bc->dflt_target; - - /* Pass the call on to the subsidiary environment. */ - if (subenv && subenv->after) subenv->after(tv, bc->subctx); -} - -/* --- @tvec_benchteardown@ --- * - * - * Arguments: @struct tvec_state *tv@ = test vector state - * @void *ctx@ = context pointer - * - * Returns: --- - * - * Use: Tear down the benchmark environment. - */ - -void tvec_benchteardown(struct tvec_state *tv, void *ctx) -{ - struct tvec_benchctx *bc = ctx; - const struct tvec_benchenv *be; - const struct tvec_env *subenv; - - if (!bc) return; - be = bc->be; subenv = be->env; - - /* Tear down any subsidiary environment. */ - if (subenv && subenv->teardown && bc->subctx) - subenv->teardown(tv, bc->subctx); - - /* If the benchmark state was temporary, then dispose of it. */ - if (bc->bst) { - if (be->bst) bc->bst->target_s = bc->dflt_target; - else { bench_destroy(bc->bst); xfree(bc->bst); } - } -} - -/*----- Measurement machinery ---------------------------------------------*/ - -/* --- @benchloop_...@ --- * - * - * Arguments: @unsigned long n@ = iteration count - * @void *ctx@ = benchmark running context - * - * Returns: --- - * - * Use: Run various kinds of benchmarking loops. - * - * * The @..._outer_...@ functions call the underlying - * function @n@ times in a loop; by contrast, the - * @..._inner_...@ functions set a register value to the - * chosen iteration count and expect the underlying function - * to perform the loop itself. - * - * * The @..._direct@ functions just call the underlying test - * function directly (though still through an `indirect - * jump' instruction); by contrast, the @..._indirect@ - * functions invoke a subsidiary environment's @run@ - * function, which adds additional overhead. - */ - -static void benchloop_outer_direct(unsigned long n, void *ctx) -{ - struct benchrun *r = ctx; - tvec_testfn *fn = r->fn; void *tctx = r->ctx; - const struct tvec_reg *in = r->in; struct tvec_reg *out = r->out; - - while (n--) fn(in, out, tctx); -} - -static void benchloop_inner_direct(unsigned long n, void *ctx) - { struct benchrun *r = ctx; *r->n = n; r->fn(r->in, r->out, r->ctx); } - -static void benchloop_outer_indirect(unsigned long n, void *ctx) -{ - struct benchrun *r = ctx; - struct tvec_state *tv = r->tv; - void (*run)(struct tvec_state *, tvec_testfn, void *) = r->env->run; - tvec_testfn *fn = r->fn; void *tctx = r->ctx; - - while (n--) run(tv, fn, tctx); -} - -static void benchloop_inner_indirect(unsigned long n, void *ctx) - { struct benchrun *r = ctx; *r->n = n; r->env->run(r->tv, r->fn, r->ctx); } - /* --- @tvec_benchrun@ --- * * * Arguments: @struct tvec_state *tv@ = test vector state @@ -372,60 +372,57 @@ void tvec_benchrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx) #undef f_any } -/*----- Output utilities --------------------------------------------------*/ - -/* --- @tvec_benchreport@ --- * +/* --- @tvec_benchafter@ --- * * - * Arguments: @const struct gprintf_ops *gops@ = print operations - * @void *go@ = print destination - * @unsigned unit@ = the unit being measured (~TVBU_...@) - * @const struct bench_timing *tm@ = the benchmark result + * Arguments: @struct tvec_state *tv@ = test vector state + * @void *ctx@ = context pointer * * Returns: --- * - * Use: Formats a report about the benchmark performance. This - * function is intended to be called on by an output - * @ebench@ function. + * Use: Invoke the subordinate environment's @after@ function to + * clean up after the benchmark. */ -void tvec_benchreport(const struct gprintf_ops *gops, void *go, - unsigned unit, const struct bench_timing *tm) +void tvec_benchafter(struct tvec_state *tv, void *ctx) { - double scale, x, n = tm->n; - const char *u, *what, *whats; + struct tvec_benchctx *bc = ctx; + const struct tvec_benchenv *be = bc->be; + const struct tvec_env *subenv = be->env; - if (!tm) { gprintf(gops, go, "benchmark FAILED"); return; } + /* Restore the benchmark state's old target. */ + bc->bst->target_s = bc->dflt_target; + bc->f &= ~TVBF_SETTRG; - assert(tm->f&BTF_TIMEOK); + /* Pass the call on to the subsidiary environment. */ + if (subenv && subenv->after) subenv->after(tv, bc->subctx); +} - switch (unit) { - case TVBU_OP: - gprintf(gops, go, "%.0f iterations ", n); - what = "op"; whats = "ops"; scale = 1000; - break; - case TVBU_BYTE: - x = n; normalize(&x, &u, 1024); gprintf(gops, go, "%.3f %sB ", x, u); - what = whats = "B"; scale = 1024; - break; - default: - abort(); - } +/* --- @tvec_benchteardown@ --- * + * + * Arguments: @struct tvec_state *tv@ = test vector state + * @void *ctx@ = context pointer + * + * Returns: --- + * + * Use: Tear down the benchmark environment. + */ - x = tm->t; normalize(&x, &u, 1000); - gprintf(gops, go, "in %.3f %ss", x, u); - if (tm->f&BTF_CYOK) { - x = tm->cy; normalize(&x, &u, 1000); - gprintf(gops, go, " (%.3f %scy)", x, u); - } - gprintf(gops, go, ": "); +void tvec_benchteardown(struct tvec_state *tv, void *ctx) +{ + struct tvec_benchctx *bc = ctx; + const struct tvec_benchenv *be; + const struct tvec_env *subenv; - x = n/tm->t; normalize(&x, &u, scale); - gprintf(gops, go, "%.3f %s%s/s", x, u, whats); - x = tm->t/n; normalize(&x, &u, 1000); - gprintf(gops, go, ", %.3f %ss/%s", x, u, what); - if (tm->f&BTF_CYOK) { - x = tm->cy/n; normalize(&x, &u, 1000); - gprintf(gops, go, " (%.3f %scy/%s)", x, u, what); + be = bc->be; subenv = be->env; + + /* Tear down any subsidiary environment. */ + if (subenv && subenv->teardown) + subenv->teardown(tv, bc->subctx); + + /* If the benchmark state was temporary, then dispose of it. */ + if (bc->bst) { + if (be->bst) bc->bst->target_s = bc->dflt_target; + else { bench_destroy(bc->bst); xfree(bc->bst); } } }