*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@ --- *
* @void *pctx@ = parent context (ignored)
* @void *ctx@ = context pointer to initialize
*
- * Returns: Zero on success, @-1@ on failure.
+ * Returns: ---
*
* Use: Initialize a benchmarking environment context.
*
* The environment description must really be a @struct
- * tvec_bench@. If the @bst@ slot is null, then a temporary
+ * tvec_benchenv@. If the @bst@ slot is null, then a temporary
* benchmark state is allocated for the current test group and
* released at the end. Otherwise, it must be the address of a
* pointer to a benchmark state: if the pointer is null, then a
* existing valid benchmark state.
*/
-int tvec_benchsetup(struct tvec_state *tv, const struct tvec_env *env,
- void *pctx, void *ctx)
+void tvec_benchsetup(struct tvec_state *tv, const struct tvec_env *env,
+ void *pctx, void *ctx)
{
struct tvec_benchctx *bc = ctx;
- const struct tvec_bench *b = (const struct tvec_bench *)env;
- const struct tvec_env *subenv = b->env;
+ const struct tvec_benchenv *be = (const struct tvec_benchenv *)env;
+ const struct tvec_env *subenv = be->env;
struct bench_timer *bt;
/* Basic initialization. */
- bc->b = b; bc->bst = 0; bc->subctx = 0;
+ bc->be = be; bc->bst = 0; bc->subctx = 0;
/* Set up the benchmarking state if it hasn't been done before. */
- if (!b->bst || !*b->bst) {
+ if (!be->bst || !*be->bst) {
bt = bench_createtimer(); if (!bt) goto fail_timer;
bc->bst = xmalloc(sizeof(*bc->bst)); bench_init(bc->bst, bt);
- if (b->bst) *b->bst = bc->bst;
- } else if (!(*b->bst)->tm)
+ if (be->bst) *be->bst = bc->bst;
+ } else if (!(*be->bst)->tm)
goto fail_timer;
else
- bc->bst = *b->bst;
+ bc->bst = *be->bst;
/* Set the default target time. */
bc->dflt_target = bc->bst->target_s;
/* Initialize the subordinate environment. */
if (subenv && subenv->ctxsz) bc->subctx = xmalloc(subenv->ctxsz);
- if (subenv && subenv->setup && subenv->setup(tv, subenv, bc, bc->subctx))
- { xfree(bc->subctx); bc->subctx = 0; return (-1); }
+ if (subenv && subenv->setup) subenv->setup(tv, subenv, bc, bc->subctx);
/* All done. */
end:
- return (0);
+ return;
fail_timer:
tvec_skipgroup(tv, "failed to create timer"); goto end;
}
*
* 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.
* 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_bench *b = (const struct tvec_bench *)env;
- const struct tvec_env *subenv = b->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 };
static const struct tvec_regdef rd =
{ "@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);
}
* Arguments: @struct tvec_state *tv@ = test vector state
* @void *ctx@ = context pointer
*
- * Returns: Zero on success, @-1@ on failure.
+ * Returns: ---
*
* Use: Invoke the subordinate environment's @before@ function to
* prepare for the benchmark.
*/
-int tvec_benchbefore(struct tvec_state *tv, void *ctx)
+void tvec_benchbefore(struct tvec_state *tv, void *ctx)
{
struct tvec_benchctx *bc = ctx;
- const struct tvec_bench *b = bc->b;
- const struct tvec_env *subenv = b->env;
+ const struct tvec_benchenv *be = bc->be;
+ const struct tvec_env *subenv = be->env;
/* Just call the subsidiary environment. */
- if (subenv && subenv->before) return (subenv->before(tv, bc->subctx));
- else return (0);
-}
-
-/* --- @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_bench *b = bc->b;
- const struct tvec_env *subenv = b->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_bench *b;
- const struct tvec_env *subenv;
-
- if (!bc) return;
- b = bc->b; subenv = b->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 (b->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);
+ if (subenv && subenv->before) subenv->before(tv, bc->subctx);
}
-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
* Returns: ---
*
* Use: Measures and reports the performance of a test function.
- *
- *
*/
void tvec_benchrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
{
struct tvec_benchctx *bc = ctx;
- const struct tvec_bench *b = bc->b;
- const struct tvec_env *subenv = b->env;
+ const struct tvec_benchenv *be = bc->be;
+ const struct tvec_env *subenv = be->env;
const struct tvec_regdef *rd;
struct tvec_output *o = tv->output;
struct bench_timing tm;
r.in = tv->in; r.out = tv->out; r.fn = fn;
/* Decide on the run function to select. */
- if (b->riter >= 0) {
- r.n = &TVEC_REG(tv, in, b->riter)->v.u;
+ if (be->riter >= 0) {
+ r.n = &TVEC_REG(tv, in, be->riter)->v.u;
loopfn = subenv && subenv->run ?
benchloop_inner_indirect : benchloop_inner_direct;
} else {
}
/* Decide on the kind of unit and the base count. */
- base = b->niter;
- if (b->rbuf < 0) unit = TVBU_OP;
- else { unit = TVBU_BYTE; base *= TVEC_REG(tv, in, b->rbuf)->v.bytes.sz; }
+ base = be->niter;
+ if (be->rbuf < 0) unit = TVBU_OP;
+ else { unit = TVBU_BYTE; base *= TVEC_REG(tv, in, be->rbuf)->v.bytes.sz; }
/* Construct a description of the test using the identifier registers. */
for (rd = tv->test->regs; rd->name; rd++)
#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); }
}
}