3 * Benchmarking in the test-vector framework
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
33 /*----- Data structures ---------------------------------------------------*/
36 struct tvec_state
*tv
;
37 const struct tvec_env
*env
;
40 const struct tvec_reg
*in
; struct tvec_reg
*out
;
44 /*----- Global variables --------------------------------------------------*/
46 struct bench_state
*tvec_benchstate
;
48 /*----- Benchmarking ------------------------------------------------------*/
50 static void normalize(double *x_inout
, const char **unit_out
, double scale
)
54 *const big
[] = { "k", "M", "G", "T", "P", "E", 0 },
55 *const little
[] = { "m", "ยต", "n", "p", "f", "a", 0 };
60 for (u
= little
, x
*= scale
; x
< 1 && u
[1]; u
++, x
*= scale
);
62 for (u
= big
, x
/= scale
; x
>= scale
&& u
[1]; u
++, x
/= scale
);
66 *x_inout
= x
; *unit_out
= *u
;
69 int tvec_benchsetup(struct tvec_state
*tv
, const struct tvec_env
*env
,
70 void *pctx
, void *ctx
)
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
;
77 bc
->b
= b
; bc
->bst
= 0; bc
->subctx
= 0;
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
)
87 bc
->dflt_target
= bc
->bst
->target_s
;
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); }
96 tvec_skipgroup(tv
, "failed to create timer"); goto end
;
99 int tvec_benchset(struct tvec_state
*tv
, const char *var
,
100 const struct tvec_env
*env
, void *ctx
)
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
} };
110 if (STRCMP(var
, ==, "@target")) {
111 if (tvty_float
.parse(&rv
, &rd
, tv
)) return (-1);
112 if (bc
) bc
->bst
->target_s
= rv
.f
;
114 } else if (subenv
&& subenv
->set
)
115 return (subenv
->set(tv
, var
, subenv
, bc
->subctx
));
120 int tvec_benchbefore(struct tvec_state
*tv
, void *ctx
)
122 struct tvec_benchctx
*bc
= ctx
;
123 const struct tvec_bench
*b
= bc
->b
;
124 const struct tvec_env
*subenv
= b
->env
;
126 if (subenv
&& subenv
->before
) return (subenv
->before(tv
, bc
->subctx
));
130 void tvec_benchafter(struct tvec_state
*tv
, void *ctx
)
132 struct tvec_benchctx
*bc
= ctx
;
133 const struct tvec_bench
*b
= bc
->b
;
134 const struct tvec_env
*subenv
= b
->env
;
136 bc
->bst
->target_s
= bc
->dflt_target
;
137 if (subenv
&& subenv
->after
) subenv
->after(tv
, bc
->subctx
);
140 void tvec_benchteardown(struct tvec_state
*tv
, void *ctx
)
142 struct tvec_benchctx
*bc
= ctx
;
143 const struct tvec_bench
*b
;
144 const struct tvec_env
*subenv
;
147 b
= bc
->b
; subenv
= b
->env
;
148 if (subenv
&& subenv
->teardown
&& bc
->subctx
)
149 subenv
->teardown(tv
, bc
->subctx
);
151 if (b
->bst
) bc
->bst
->target_s
= bc
->dflt_target
;
152 else { bench_destroy(bc
->bst
); xfree(bc
->bst
); }
156 static void benchloop_outer_direct(unsigned long n
, void *p
)
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
;
162 while (n
--) fn(in
, out
, ctx
);
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
); }
168 static void benchloop_outer_indirect(unsigned long n
, void *p
)
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
;
175 while (n
--) run(tv
, fn
, ctx
);
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
); }
181 void tvec_benchrun(struct tvec_state
*tv
, tvec_testfn
*fn
, void *ctx
)
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
;
197 r
.tv
= tv
; r
.env
= subenv
; r
.ctx
= bc
->subctx
;
198 r
.in
= tv
->in
; r
.out
= tv
->out
; r
.fn
= fn
;
201 r
.n
= &TVEC_REG(tv
, in
, b
->riter
)->v
.u
;
202 loopfn
= subenv
&& subenv
->run ?
203 benchloop_inner_indirect
: benchloop_inner_direct
;
206 loopfn
= subenv
&& subenv
->run ?
207 benchloop_outer_indirect
: benchloop_outer_direct
;
211 if (b
->rbuf
< 0) unit
= TVBU_OP
;
212 else { unit
= TVBU_BYTE
; base
*= TVEC_REG(tv
, in
, b
->rbuf
)->v
.bytes
.sz
; }
214 for (rd
= tv
->test
->regs
; rd
->name
; rd
++)
216 if (f
&f_any
) dstr_puts(&d
, ", ");
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
);
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);
227 o
->ops
->ebench(o
, d
.buf
, unit
, &tm
);
234 void tvec_benchreport(const struct gprintf_ops
*gops
, void *go
,
235 unsigned unit
, const struct bench_timing
*tm
)
237 double scale
, x
, n
= tm
->n
;
238 const char *u
, *what
, *whats
;
240 if (!tm
) { gprintf(gops
, go
, "benchmark FAILED"); return; }
242 assert(tm
->f
&BTF_TIMEOK
);
246 gprintf(gops
, go
, "%.0f iterations ", n
);
247 what
= "op"; whats
= "ops"; scale
= 1000;
250 x
= n
; normalize(&x
, &u
, 1024); gprintf(gops
, go
, "%.3f %sB ", x
, u
);
251 what
= whats
= "B"; scale
= 1024;
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
);
263 gprintf(gops
, go
, ": ");
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
);
275 /*----- That's all, folks -------------------------------------------------*/