@@@ BROKEN wip
[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 -------------------------------------------------*/