+/* --- @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);
+ }
+}
+