chiark / gitweb /
@@@ wip
[mLib] / test / tvec-bench.c
index ab21b9c654f72060d67d982dd8f1cc6770c4aa51..b56fcbe4a904e22a1cdb9103655d4278a2eb32a2 100644 (file)
 /*----- Data structures ---------------------------------------------------*/
 
 struct benchrun {
-  struct tvec_state *tv;
-  const struct tvec_env *env;
-  void *ctx;
-  unsigned long *n;
-  const struct tvec_reg *in; struct tvec_reg *out;
-  tvec_testfn *fn;
+  struct tvec_state *tv;               /* test vector state */
+  const struct tvec_env *env;          /* subordinate environment */
+  void *ctx;                           /* subordinate env's context */
+  unsigned long *n;                    /* iteration count address */
+  const struct tvec_reg *in; struct tvec_reg *out; /* register vectors */
+  tvec_testfn *fn;                     /* test function to run */
 };
 
 /*----- Global variables --------------------------------------------------*/
 
-struct bench_state *tvec_benchstate;
+struct bench_state *tvec_benchstate;   /* common benchmarking state */
 
-/*----- Benchmarking ------------------------------------------------------*/
+/*----- Utilities ---------------------------------------------------------*/
+
+/* --- @normalize@ --- *
+ *
+ * Arguments:  @double *x_inout@ = address of a value to normalize
+ *             @const char **unit_out@ = address to store unit prefix
+ *             @double scale@ = scale factor for unit steps
+ *
+ * Returns:    ---
+ *
+ * Use:                Adjust @*x_inout@ by a power of @scale@, and set @*unit_out@
+ *             so that printing the two reflects the original value with an
+ *             appropriate SI unit scaling.  The @scale@ should be 1024 for
+ *             binary quantities, most notably memory sizes, or 1000 for
+ *             other quantities.
+ */
 
 static void normalize(double *x_inout, const char **unit_out, double scale)
 {
@@ -66,6 +81,29 @@ static void normalize(double *x_inout, const char **unit_out, double scale)
   *x_inout = x; *unit_out = *u;
 }
 
+/*----- Benchmark environment scaffolding ---------------------------------*/
+
+/* --- @tvec_benchsetup@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test vector state
+ *             @const struct tvec_env *env@ = environment description
+ *             @void *pctx@ = parent context (ignored)
+ *             @void *ctx@ = context pointer to initialize
+ *
+ * Returns:    Zero on success, @-1@ on failure.
+ *
+ * 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
+ *             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
+ *             fresh state is allocated and initialized and the pointer is
+ *             updated; otherwise, the pointer is assumed to refer to an
+ *             existing valid benchmark state.
+ */
+
 int tvec_benchsetup(struct tvec_state *tv, const struct tvec_env *env,
                    void *pctx, void *ctx)
 {
@@ -74,8 +112,10 @@ int tvec_benchsetup(struct tvec_state *tv, const struct tvec_env *env,
   const struct tvec_env *subenv = b->env;
   struct bench_timer *bt;
 
+  /* Basic initialization. */
   bc->b = b; bc->bst = 0; bc->subctx = 0;
 
+  /* Set up the benchmarking state if it hasn't been done before. */
   if (!b->bst || !*b->bst) {
     bt = bench_createtimer(); if (!bt) goto fail_timer;
     bc->bst = xmalloc(sizeof(*bc->bst)); bench_init(bc->bst, bt);
@@ -84,18 +124,41 @@ int tvec_benchsetup(struct tvec_state *tv, const struct tvec_env *env,
     goto fail_timer;
   else
     bc->bst = *b->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); }
 
+  /* All done. */
 end:
   return (0);
 fail_timer:
   tvec_skipgroup(tv, "failed to create timer"); goto end;
 }
 
+/* --- @tvec_benchset@ --- *
+ *
+ * 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.
+ *
+ * Use:                Set a special variable.  The following special variables are
+ *             supported.
+ *
+ *               * %|@target|% is the (approximate) number of seconds to run
+ *                 the benchmark.
+ *
+ *             Unrecognized variables are passed to the subordinate
+ *             environment, if there is one.
+ */
+
 int tvec_benchset(struct tvec_state *tv, const char *var,
                  const struct tvec_env *env, void *ctx)
 {
@@ -117,26 +180,62 @@ int tvec_benchset(struct tvec_state *tv, const char *var,
     return (0);
 }
 
+/* --- @tvec_benchbefore@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test vector state
+ *             @void *ctx@ = context pointer
+ *
+ * Returns:    Zero on success, @-1@ on failure.
+ *
+ * Use:                Invoke the subordinate environment's @before@ function to
+ *             prepare for the benchmark.
+ */
+
 int 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;
 
+  /* 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;
@@ -145,38 +244,79 @@ void tvec_benchteardown(struct tvec_state *tv, void *ctx)
 
   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); }
   }
 }
 
-static void benchloop_outer_direct(unsigned long n, void *p)
+/*----- 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 = p;
-  tvec_testfn *fn = r->fn; void *ctx = r->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, ctx);
+  while (n--) fn(in, out, tctx);
 }
 
-static void benchloop_inner_direct(unsigned long n, void *p)
-  { struct benchrun *r = p; *r->n = n; r->fn(r->in, r->out, r->ctx); }
+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 *p)
+static void benchloop_outer_indirect(unsigned long n, void *ctx)
 {
-  struct benchrun *r = p;
+  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 *ctx = r->ctx;
+  tvec_testfn *fn = r->fn; void *tctx = r->ctx;
 
-  while (n--) run(tv, fn, ctx);
+  while (n--) run(tv, fn, tctx);
 }
 
-static void benchloop_inner_indirect(unsigned long n, void *p)
-  { struct benchrun *r = p; *r->n = n; r->env->run(r->tv, r->fn, r->ctx); }
+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
+ *             @tvec_testfn *fn@ = test function to run
+ *             @void *ctx@ = context pointer for the test function
+ *
+ * Returns:    ---
+ *
+ * Use:                Measures and reports the performance of a test function.
+ *
+ *
+ */
 
 void tvec_benchrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
 {
@@ -194,9 +334,11 @@ void tvec_benchrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
   unsigned f = 0;
 #define f_any 1u
 
+  /* Fill in the easy parts of the run context. */
   r.tv = tv; r.env = subenv; r.ctx = bc->subctx;
   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;
     loopfn = subenv && subenv->run ?
@@ -207,10 +349,12 @@ void tvec_benchrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
       benchloop_outer_indirect : benchloop_outer_direct;
   }
 
+  /* 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; }
 
+  /* Construct a description of the test using the identifier registers. */
   for (rd = tv->test->regs; rd->name; rd++)
     if (rd->f&TVRF_ID) {
       if (f&f_any) dstr_puts(&d, ", ");
@@ -220,6 +364,7 @@ void tvec_benchrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
                   TVSF_COMPACT, &dstr_printops, &d);
     }
 
+  /* Run the benchmark. */
   o->ops->bbench(o, d.buf, unit);
   if (bench_measure(&tm, bc->bst, base, loopfn, &r))
     o->ops->ebench(o, d.buf, unit, 0);
@@ -231,6 +376,22 @@ void tvec_benchrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
 #undef f_any
 }
 
+/*----- 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)
 {