chiark / gitweb /
@@@ so much mess
[mLib] / test / tvec-bench.c
1 /* -*-c-*-
2  *
3  * Benchmarking in the test-vector framework
4  *
5  * (c) 2023 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
12  * mLib is free software: you can redistribute it and/or modify it under
13  * the terms of the GNU Library General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or (at
15  * your option) any later version.
16  *
17  * mLib is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
20  * License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with mLib.  If not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25  * USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include "bench.h"
31 #include "tvec.h"
32
33 /*----- Data structures ---------------------------------------------------*/
34
35 struct benchrun {
36   struct tvec_state *tv;
37   const struct tvec_env *env;
38   void *ctx;
39   unsigned long *n;
40   const struct tvec_reg *in; struct tvec_reg *out;
41   tvec_testfn *fn;
42 };
43
44 /*----- Global variables --------------------------------------------------*/
45
46 struct bench_state *tvec_benchstate;
47
48 /*----- Benchmarking ------------------------------------------------------*/
49
50 static void normalize(double *x_inout, const char **unit_out, double scale)
51 {
52   static const char
53     *const nothing = "",
54     *const big[] = { "k", "M", "G", "T", "P", "E", 0 },
55     *const little[] = { "m", "ยต", "n", "p", "f", "a", 0 };
56   const char *const *u;
57   double x = *x_inout;
58
59   if (x < 1)
60     for (u = little, x *= scale; x < 1 && u[1]; u++, x *= scale);
61   else if (x >= scale)
62     for (u = big, x /= scale; x >= scale && u[1]; u++, x /= scale);
63   else
64     u = &nothing;
65
66   *x_inout = x; *unit_out = *u;
67 }
68
69 int tvec_benchsetup(struct tvec_state *tv, const struct tvec_env *env,
70                     void *pctx, void *ctx)
71 {
72   struct tvec_benchctx *bc = ctx;
73   const struct tvec_bench *b = (const struct tvec_bench *)env;
74   const struct tvec_env *subenv = b->env;
75   struct bench_timer *bt;
76
77   bc->b = b; bc->bst = 0; bc->subctx = 0;
78
79   if (!b->bst || !*b->bst) {
80     bt = bench_createtimer(); if (!bt) goto fail_timer;
81     bc->bst = xmalloc(sizeof(*bc->bst)); bench_init(bc->bst, bt);
82     if (b->bst) *b->bst = bc->bst;
83   } else if (!(*b->bst)->tm)
84     goto fail_timer;
85   else
86     bc->bst = *b->bst;
87   bc->dflt_target = bc->bst->target_s;
88
89   if (subenv && subenv->ctxsz) bc->subctx = xmalloc(subenv->ctxsz);
90   if (subenv && subenv->setup && subenv->setup(tv, subenv, bc, bc->subctx))
91     { xfree(bc->subctx); bc->subctx = 0; return (-1); }
92
93 end:
94   return (0);
95 fail_timer:
96   tvec_skipgroup(tv, "failed to create timer"); goto end;
97 }
98
99 int tvec_benchset(struct tvec_state *tv, const char *var,
100                   const struct tvec_env *env, void *ctx)
101 {
102   struct tvec_benchctx *bc = ctx;
103   const struct tvec_bench *b = (const struct tvec_bench *)env;
104   const struct tvec_env *subenv = b->env;
105   union tvec_regval rv;
106   static const struct tvec_floatinfo fi = { TVFF_NOMAX, 0.0, 0.0, 0.0 };
107   static const struct tvec_regdef rd =
108     { "@target", -1, &tvty_float, 0, { &fi } };
109
110   if (STRCMP(var, ==, "@target")) {
111     if (tvty_float.parse(&rv, &rd, tv)) return (-1);
112     if (bc) bc->bst->target_s = rv.f;
113     return (1);
114   } else if (subenv && subenv->set)
115     return (subenv->set(tv, var, subenv, bc->subctx));
116   else
117     return (0);
118 }
119
120 int tvec_benchbefore(struct tvec_state *tv, void *ctx)
121 {
122   struct tvec_benchctx *bc = ctx;
123   const struct tvec_bench *b = bc->b;
124   const struct tvec_env *subenv = b->env;
125
126   if (subenv && subenv->before) return (subenv->before(tv, bc->subctx));
127   else return (0);
128 }
129
130 void tvec_benchafter(struct tvec_state *tv, void *ctx)
131 {
132   struct tvec_benchctx *bc = ctx;
133   const struct tvec_bench *b = bc->b;
134   const struct tvec_env *subenv = b->env;
135
136   bc->bst->target_s = bc->dflt_target;
137   if (subenv && subenv->after) subenv->after(tv, bc->subctx);
138 }
139
140 void tvec_benchteardown(struct tvec_state *tv, void *ctx)
141 {
142   struct tvec_benchctx *bc = ctx;
143   const struct tvec_bench *b;
144   const struct tvec_env *subenv;
145
146   if (!bc) return;
147   b = bc->b; subenv = b->env;
148   if (subenv && subenv->teardown && bc->subctx)
149     subenv->teardown(tv, bc->subctx);
150   if (bc->bst) {
151     if (b->bst) bc->bst->target_s = bc->dflt_target;
152     else { bench_destroy(bc->bst); xfree(bc->bst); }
153   }
154 }
155
156 static void benchloop_outer_direct(unsigned long n, void *p)
157 {
158   struct benchrun *r = p;
159   tvec_testfn *fn = r->fn; void *ctx = r->ctx;
160   const struct tvec_reg *in = r->in; struct tvec_reg *out = r->out;
161
162   while (n--) fn(in, out, ctx);
163 }
164
165 static void benchloop_inner_direct(unsigned long n, void *p)
166   { struct benchrun *r = p; *r->n = n; r->fn(r->in, r->out, r->ctx); }
167
168 static void benchloop_outer_indirect(unsigned long n, void *p)
169 {
170   struct benchrun *r = p;
171   struct tvec_state *tv = r->tv;
172   void (*run)(struct tvec_state *, tvec_testfn, void *) = r->env->run;
173   tvec_testfn *fn = r->fn; void *ctx = r->ctx;
174
175   while (n--) run(tv, fn, ctx);
176 }
177
178 static void benchloop_inner_indirect(unsigned long n, void *p)
179   { struct benchrun *r = p; *r->n = n; r->env->run(r->tv, r->fn, r->ctx); }
180
181 void tvec_benchrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
182 {
183   struct tvec_benchctx *bc = ctx;
184   const struct tvec_bench *b = bc->b;
185   const struct tvec_env *subenv = b->env;
186   const struct tvec_regdef *rd;
187   struct tvec_output *o = tv->output;
188   struct bench_timing tm;
189   struct benchrun r;
190   bench_fn *loopfn;
191   unsigned unit;
192   dstr d = DSTR_INIT;
193   double base;
194   unsigned f = 0;
195 #define f_any 1u
196
197   r.tv = tv; r.env = subenv; r.ctx = bc->subctx;
198   r.in = tv->in; r.out = tv->out; r.fn = fn;
199
200   if (b->riter >= 0) {
201     r.n = &TVEC_REG(tv, in, b->riter)->v.u;
202     loopfn = subenv && subenv->run ?
203       benchloop_inner_indirect : benchloop_inner_direct;
204   } else {
205     r.n = 0;
206     loopfn = subenv && subenv->run ?
207       benchloop_outer_indirect : benchloop_outer_direct;
208   }
209
210   base = b->niter;
211   if (b->rbuf < 0) unit = TVBU_OP;
212   else { unit = TVBU_BYTE; base *= TVEC_REG(tv, in, b->rbuf)->v.bytes.sz; }
213
214   for (rd = tv->test->regs; rd->name; rd++)
215     if (rd->f&TVRF_ID) {
216       if (f&f_any) dstr_puts(&d, ", ");
217       else f |= f_any;
218       dstr_putf(&d, "%s = ", rd->name);
219       rd->ty->dump(&TVEC_REG(tv, in, rd->i)->v, rd,
220                    TVSF_COMPACT, &dstr_printops, &d);
221     }
222
223   o->ops->bbench(o, d.buf, unit);
224   if (bench_measure(&tm, bc->bst, base, loopfn, &r))
225     o->ops->ebench(o, d.buf, unit, 0);
226   else
227     o->ops->ebench(o, d.buf, unit, &tm);
228
229   dstr_destroy(&d);
230
231 #undef f_any
232 }
233
234 void tvec_benchreport(const struct gprintf_ops *gops, void *go,
235                       unsigned unit, const struct bench_timing *tm)
236 {
237   double scale, x, n = tm->n;
238   const char *u, *what, *whats;
239
240   if (!tm) { gprintf(gops, go, "benchmark FAILED"); return; }
241
242   assert(tm->f&BTF_TIMEOK);
243
244   switch (unit) {
245     case TVBU_OP:
246       gprintf(gops, go, "%.0f iterations ", n);
247       what = "op"; whats = "ops"; scale = 1000;
248       break;
249     case TVBU_BYTE:
250       x = n; normalize(&x, &u, 1024); gprintf(gops, go, "%.3f %sB ", x, u);
251       what = whats = "B"; scale = 1024;
252       break;
253     default:
254       abort();
255   }
256
257   x = tm->t; normalize(&x, &u, 1000);
258   gprintf(gops, go, "in %.3f %ss", x, u);
259   if (tm->f&BTF_CYOK) {
260     x = tm->cy; normalize(&x, &u, 1000);
261     gprintf(gops, go, " (%.3f %scy)", x, u);
262   }
263   gprintf(gops, go, ": ");
264
265   x = n/tm->t; normalize(&x, &u, scale);
266   gprintf(gops, go, "%.3f %s%s/s", x, u, whats);
267   x = tm->t/n; normalize(&x, &u, 1000);
268   gprintf(gops, go, ", %.3f %ss/%s", x, u, what);
269   if (tm->f&BTF_CYOK) {
270     x = tm->cy/n; normalize(&x, &u, 1000);
271     gprintf(gops, go, " (%.3f %scy/%s)", x, u, what);
272   }
273 }
274
275 /*----- That's all, folks -------------------------------------------------*/