# 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 */
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*/);
/* 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@ --- *
*
/* --- @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; } \
}) \
{ ; }, \
{ 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@ --- *
*
/* --- @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.
* 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
* @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@ --- *
*
* 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@.
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
/*----- 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