@@@ mostly bench docs
[mLib] / test / bench.h
index 3756247..05c1eee 100644 (file)
 #  include "dstr.h"
 #endif
 
+#ifndef MLIB_MACROS_H
+#  include "macros.h"
+#endif
+
 #ifndef MLIB_GPRINTF_H
 #  include "gprintf.h"
 #endif
 
-/*----- Data structures ---------------------------------------------------*/
+/*----- Timers ------------------------------------------------------------*/
 
 struct bench_time {
   unsigned f;                          /* flags */
@@ -73,7 +77,10 @@ struct bench_timing {
   double n, t, cy;                     /* count, time, and cycles */
 };
 
-struct bench_timer { const struct bench_timerops *ops; };
+struct bench_timer {
+  const struct bench_timerops *ops;
+  unsigned ref;
+};
 
 struct bench_timerops {
   void (*describe)(struct bench_timer */*bt*/, dstr */*d*/);
@@ -105,24 +112,22 @@ struct bench_timerops {
     /* Release the timer and any resources it holds. */
 };
 
-struct bench_state {
-  struct bench_timer *tm;              /* a timer */
-  double target_s;                     /* target time to run benchmarks */
-  unsigned f;                          /* calibration flags (@BTF_...@) */
-#define BTF_CLB 0x0100                 /*   tried calibrating */
-  struct { double m, c; } clk, cy;     /* calculated overheads */
-};
-
-typedef void bench_fn(unsigned long /*n*/, void */*ctx*/);
-  /* Run the benchmark @n@ times, given a context pointer @ctx@. */
-
-enum {
-  BTU_OP,                             /* counting operations of some kind */
-  BTU_BYTE,                            /* counting bytes (@rbuf >= 0@) */
-  BTU_LIMIT                            /* (number of units) */
-};
+/* --- @bench_createtimer@ --- *
+ *
+ * Arguments:  @const char *config@ = user-supplied configuration string
+ *
+ * Returns:    A freshly constructed standard timer object, or null on
+ *             failure.
+ *
+ * Use:                Allocate a timer.  Dispose of it by calling
+ *             @tm->ops->destroy(tm)@ when you're done.
+ *
+ *             Applications should not set configuration strings except as
+ *             established by user action, e.g., from a command-line option,
+ *             environment variable, or configuration file.
+ */
 
-/*----- Macros ------------------------------------------------------------*/
+extern struct bench_timer *bench_createtimer(const char *config);
 
 /* --- @BENCH_TIMELOOP_DECLS@ --- *
  *
@@ -140,28 +145,28 @@ enum {
 /* --- @BENCH_TIMELOOP_TAG@ --- *
  *
  * Arguments:  @tag@ = control structure tag
- *             @struct bench_state *b@ = benchmark state
+ *             @struct bench_state *tm@ = benchmark timer
  *             @struct bench_timing *delta_out@ = where to put the result
  *             @double n@ = number of iterations
  *
  * Returns:    ---
  *
- * Use:                @BENCH_TIMELOOP_TAG(tag, b, delta_out, n) stmt@ runs @stmt@
+ * Use:                @BENCH_TIMELOOP_TAG(tag, tm, delta_out, n) stmt@ runs @stmt@
  *             @n@ times, capturing the time and/or CPU cycles taken in
  *             @*delta_out@.  The iteration count must be no more than the
  *             %%\emph{square}%% of @ULONG_MAX@.  If @stmt@ executes a free
- *             @break@ statement, then the containing loop -- which ust
+ *             @break@ statement, then the containing loop -- which must
  *             exist -- is exited.
  *
  *             This macro is not intended to be used directly by users: it
  *             is used internally by @bench_calibrate@ and @BENCH_MEASURE@.
  */
 
-#define BENCH_TIMELOOP_TAG(tag, b, delta_out, n)                       \
+#define BENCH_TIMELOOP_TAG(tag, tm, delta_out, n, onbreak)             \
   MC_BEFORE(tag##__benchtl_setup, {                                    \
     double _R = ULONG_MAX; double _n = (n);                            \
                                                                        \
-    _bench_tm = (b)->tm;                                               \
+    _bench_tm = (tm);                                                  \
     if (_n <= _R) { _bench_n1 = 0; _bench_n = _n; }                    \
     else { _bench_n1 = _n/_R; _bench_n = _n - _R*_bench_n1; }          \
   })                                                                   \
@@ -178,131 +183,18 @@ enum {
        { ; },                                                          \
        { MC_GOTARGET(tag##__benchtl_break); })
 
-/* --- @BENCH_MEASURE_DECLS@ --- *
- *
- * Arguments:  ---
- *
- * Use:                Expands to the declarations needed by @BENCH_MEASURE@.
- *             These should be at block scope, not at toplevel.
- */
-
-#define BENCH_MEASURE_DECLS                                            \
-  struct bench_state *_bench_b;                                                \
-  struct bench_timing *_bench_t;                                       \
-  double _bench_nn;                                                    \
-  BENCH_TIMELOOP_DECLS
-
-/* --- @BENCH_MEASURE@, @BENCH_MEASURE_TAG@ --- *
- *
- * Arguments:  @tag@ = control structure tag
- *             @struct bench_state *b@ = benchmark state
- *             @int &rc@ = where to put the result code (zero on success,
- *                     %$-1$% on failure)
- *             @struct bench_timing *t_out@ = where to put the result
- *             @double base@ = number of operations per external iteration
- *
- * Returns:    ---
- *
- * Use:                @BENCH_MEASURE(b, rc, delta_out, n) stmt@ measures the
- *             execution of @stmt@.
- *
- *             The statement should perform @_bench_n@ iterations of some
- *             operation.  It will be invoked multiple times, with varying
- *             iteration counts, so as to run for approximately
- *             @b->target_s@ seconds.
- *
- *             On success, the resulting timing is left in @*t_out@, with
- *             @t_out->n@ holding the product of the final iteration count
- *             and @base@, and @rc@ is set to zero.  If the timer fails, or
- *             if @stmt@ invokes a free @break@ statement, then @rc@ is set
- *             to %$-1$%.
- *
- *             The macro @BENCH_MEASURE_TAG@ is the same, except that it
- *             allows an explicit control-structure tag to be set so that it
- *             can be used within larger control structure macros.
- */
-
-#define BENCH_MEASURE_TAG(tag, b, rc, t_out, base)                     \
-  MC_BEFORE(tag##__benchmsr_setup, {                                   \
-    _bench_b = (b); _bench_t = (t_out); _bench_nn = 1.0;               \
-    if (bench_preflight(_bench_b)) MC_GOTARGET(tag##__benchmsr_fail);  \
-  })                                                                   \
-  MC_TARGET(tag##__benchmsr_done,                                      \
-    { bench_adjust(_bench_b, _bench_t, _bench_nn, (base)); (rc) = 0; }) \
-  MC_TARGET(tag##__benchmsr_fail, { (rc) = -1; })                      \
-  for (;;)                                                             \
-    MC_WRAP(tag##__benchmsr_loop,                                      \
-      { ; },                                                           \
-      { _bench_t->f &= _bench_b->f;                                    \
-       if (!(_bench_t->f&BTF_TIMEOK)) MC_GOTARGET(tag##__benchmsr_fail); \
-       if (bench_adapt(_bench_b, &_bench_nn, _bench_t))                \
-         MC_GOTARGET(tag##__benchmsr_done);                            \
-      },                                                               \
-      { MC_GOTARGET(tag##__benchmsr_fail); })                          \
-    BENCH_TIMELOOP_TAG(tag##__benchmsr_time, _bench_b, _bench_t, _bench_nn)
-
-#define BENCH_MEASURE(b, rc, t_out, base)                              \
-  BENCH_MEASURE_TAG(bench_measure, b, rc, t_out, base)
-
-/* --- @BENCHMARK_DECLS@ --- *
- *
- * Arguments:  ---
- *
- * Use:                Expands to the declarations needed by @BENCHMARK_TAG@.
- *             These should be at block scope, not at toplevel.
- */
-
-#define BENCHMARK_DECLS                                                        \
-  struct bench_timing _bench_tm;                                       \
-  int _bench_rc;                                                       \
-  BENCH_MEASURE_DECLS
-
-/* --- @BENCHMARK_TAG@ --- *
- *
- * Arguments:  @tag@ = control structure tag
- *             @struct bench_state *b@ = benchmark state
- *             @struct gprintf_ops *gops, void *go@ = output formatter
- *             @unsigned unit@ = unit being measured (@BTU_...@ code)
- *             @double base@ = number of units per external iteration
- *
- * Returns:    ---
- *
- * Use:                @BENCHMARK_TAG(tag, b, gops, go, unit, base) stmt@ measures
- *             the execution of @stmt@ and writes a report to an output
- *             formatter.  The @stmt@ should run @_bench_n@ iterations of
- *             the operation to be measured.
- *
- *             No tagless version of this macro is provided, because it is
- *             expected to be useful primarily in the construction of
- *             higher-level macros.
- */
-
-#define BENCHMARK_TAG(tag, b, gops, go, unit, base)                    \
-  MC_AFTER(tag##__benchmark_after, {                                   \
-    if (_bench_rc) gprintf((gops), (go), "FAILED");                    \
-    else bench_report((gops), (go), _bench_b, (unit), &_bench_tm);     \
-  })                                                                   \
-  BENCH_MEASURE_TAG(tag##__benchmark_measure,                          \
-                   (b), _bench_rc, &_bench_tm, (base))
-
-/*----- Functions provided ------------------------------------------------*/
+/*----- Measuring ---------------------------------------------------------*/
 
-/* --- @bench_createtimer@ --- *
- *
- * Arguments:  @const char *config@ = user-supplied configuration string
- *
- * Returns:    A freshly constructed standard timer object, or null on
- *             failure.
- *
- * Use:                Allocate a timer.  Dispose of it by calling
- *             @tm->ops->destroy(tm)@ when you're done.
- *
- *             Applications should not set configuration strings except as
- *             established by user action, e.g., from a command-line option,
- *             environment variable, or configuration file.
- */
+struct bench_state {
+  struct bench_timer *tm;              /* a timer */
+  double target_s;                     /* target time to run benchmarks */
+  unsigned f;                          /* calibration flags (@BTF_...@) */
+#define BTF_CLB 0x0100                 /*   tried calibrating */
+  struct { double m, c; } clk, cy;     /* calculated overheads */
+};
 
-extern struct bench_timer *bench_createtimer(const char *config);
+typedef void bench_fn(unsigned long /*n*/, void */*ctx*/);
+  /* Run the benchmark @n@ times, given a context pointer @ctx@. */
 
 /* --- @bench_init@ --- *
  *
@@ -377,8 +269,8 @@ extern int bench_preflight(struct bench_state */*b*/);
 
 /* --- @bench_adapt@ --- *
  *
- * Arguments:  @struct bench_state *b@ = benchmark state
- *             @double *n_inout@ = number of iterations, updated
+ * Arguments:  @double *n_inout@ = number of iterations, updated
+ *             @double target_s@ = target time in seconds
  *             @const struct bench_timing *t@ = timing from the previous run
  *
  * Returns:    Nonzero if the measurement is sufficient; zero to run again.
@@ -387,17 +279,20 @@ extern int bench_preflight(struct bench_state */*b*/);
  *             benchmark function to perform next.  It is used in a loop
  *             such as the following.
  *
- *                     @double n = 1.0;@
+ *                     @BENCH_TIMELOOP_DECLS;@
+ *                     @struct bench_timer *tm;@
  *                     @struct bench_timing t;@
+ *                     @double n = 1.0, target_s = 1.0;@
  *
  *                     @do {@
- *                       (run @n@ iterations; set @t@ to the timing)
- *                     @} while (!bench_adapt(b, &n, &t));@
+ *                       @BENCH_TIMELOOP_TAG(time, tm, &t, n, { break; })@
+ *                         (do @_bench_n@ iterations of some computation)@;@
+ *                     @} while (!bench_adapt(&n, target_s, &t));@
  *
  *             On entry, @*n_inout@ should be the number of iterations
  *             performed by the previous pass, and @*t@ the resulting time;
- *             the @BTF_TIMEOK@ flag must be set @t->f@.  If the timing is
- *             sufficient -- @t->t@ is sufficiently close to @b->target_s@
+ *             the @BTF_TIMEOK@ flag must be set in @t->f@.  If the timing
+ *             is sufficient -- @t->t@ is sufficiently close to @target_s@
  *             -- then the function returns nonzero to indicate that
  *             measurement is complete.  Otherwise, it sets @*n_inout@ to a
  *             new, larger iteration count and returns zero to indicate that
@@ -408,7 +303,7 @@ extern int bench_preflight(struct bench_state */*b*/);
  *             @bench_measure@ function.
  */
 
-extern int bench_adapt(struct bench_state */*b*/, double */*n_inout*/,
+extern int bench_adapt(double */*n_inout*/, double /*target_s*/,
                       const struct bench_timing */*t*/);
 
 /* --- @bench_adjust@ --- *
@@ -421,7 +316,7 @@ extern int bench_adapt(struct bench_state */*b*/, double */*n_inout*/,
  *
  * Returns:    ---
  *
- * Use:                Adjusts a raw timing, as captured by @BENCH_TIMELOOP@,
+ * Use:                Adjusts a raw timing, as captured by @BENCH_TIMELOOP_TAG@,
  *             according to the calibration data captured in @b@.
  *             On exit, the timing data is updated, and @t->n@ is set to the
  *             product @n*base@.
@@ -435,6 +330,73 @@ extern void bench_adjust(struct bench_state */*b*/,
                         struct bench_timing */*t_inout*/,
                         double /*n*/, double /*base*/);
 
+/* --- @BENCH_MEASURE_DECLS@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Use:                Expands to the declarations needed by @BENCH_MEASURE@.
+ *             These should be at block scope, not at toplevel.
+ */
+
+#define BENCH_MEASURE_DECLS                                            \
+  struct bench_state *_bench_b;                                                \
+  struct bench_timing *_bench_t;                                       \
+  double _bench_nn;                                                    \
+  BENCH_TIMELOOP_DECLS
+
+/* --- @BENCH_MEASURE@, @BENCH_MEASURE_TAG@ --- *
+ *
+ * Arguments:  @tag@ = control structure tag
+ *             @struct bench_state *b@ = benchmark state
+ *             @int &rc@ = where to put the result code (zero on success,
+ *                     %$-1$% on failure)
+ *             @struct bench_timing *t_out@ = where to put the result
+ *             @double base@ = number of operations per external iteration
+ *
+ * Returns:    ---
+ *
+ * Use:                @BENCH_MEASURE(b, rc, delta_out, n) stmt@ measures the
+ *             execution of @stmt@.
+ *
+ *             The statement should perform @_bench_n@ iterations of some
+ *             operation.  It will be invoked multiple times, with varying
+ *             iteration counts, so as to run for approximately
+ *             @b->target_s@ seconds.
+ *
+ *             On success, the resulting timing is left in @*t_out@, with
+ *             @t_out->n@ holding the product of the final iteration count
+ *             and @base@, and @rc@ is set to zero.  If the timer fails, or
+ *             if @stmt@ invokes a free @break@ statement, then @rc@ is set
+ *             to %$-1$%.
+ *
+ *             The macro @BENCH_MEASURE_TAG@ is the same, except that it
+ *             allows an explicit control-structure tag to be set so that it
+ *             can be used within larger control structure macros.
+ */
+
+#define BENCH_MEASURE_TAG(tag, b, rc, t_out, base)                     \
+  MC_BEFORE(tag##__benchmsr_setup, {                                   \
+    _bench_b = (b); _bench_t = (t_out); _bench_nn = 1.0;               \
+    if (bench_preflight(_bench_b)) MC_GOTARGET(tag##__benchmsr_fail);  \
+  })                                                                   \
+  MC_TARGET(tag##__benchmsr_done,                                      \
+    { bench_adjust(_bench_b, _bench_t, _bench_nn, (base)); (rc) = 0; }) \
+  MC_TARGET(tag##__benchmsr_fail, { (rc) = -1; })                      \
+  for (;;)                                                             \
+    MC_WRAP(tag##__benchmsr_loop,                                      \
+      { ; },                                                           \
+      { _bench_t->f &= _bench_b->f;                                    \
+       if (!(_bench_t->f&BTF_TIMEOK)) MC_GOTARGET(tag##__benchmsr_fail); \
+       if (bench_adapt(&_bench_nn, _bench_b->target_s, _bench_t))      \
+         MC_GOTARGET(tag##__benchmsr_done);                            \
+      },                                                               \
+      { MC_GOTARGET(tag##__benchmsr_fail); })                          \
+    BENCH_TIMELOOP_TAG(tag##__benchmsr_time,                           \
+                      _bench_b->tm, _bench_t, _bench_nn, { break; })
+
+#define BENCH_MEASURE(b, rc, t_out, base)                              \
+  BENCH_MEASURE_TAG(bench_measure, b, rc, t_out, base)
+
 /* --- @bench_measure@ --- *
  *
  * Arguments:  @struct bench_state *b@ = benchmark state
@@ -460,6 +422,12 @@ extern int bench_measure(struct bench_state */*b*/,
 
 /*----- Reporting ---------------------------------------------------------*/
 
+enum {
+  BTU_OP,                             /* counting operations of some kind */
+  BTU_BYTE,                            /* counting bytes (@rbuf >= 0@) */
+  BTU_LIMIT                            /* (number of units) */
+};
+
 /* --- @bench_report@ --- *
  *
  * Arguments:  @const struct gprintf_ops *gops, void *gp@ = output formatter