cheatah
Source

tests/benchmarks/fixed_glm_bench.cpp

1// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).
2// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.
3//
4// cheatah::fixarray::Fixed vs GLM — the COMPLETE overlap of the two APIs, at every size and both
5// precisions. glm_compare_bench.cpp measures the dynamic NDArray on GLM's home turf (and loses on
6// purpose: a heap allocation to move sixteen doubles). This file measures the fixed-extent types,
7// which exist precisely so that comparison is winnable.
8//
9// Every operation appears as a `_fixed` / `_glm` pair with identical inputs and identical work, so a
10// regression anywhere is a single line to read. Both sides compile with the same flags in the same
11// TU: no -march=native, so neither gets an ISA the other lacks, and any difference is code shape.
12//
13// Inputs are re-read through DoNotOptimize each iteration, so nothing is hoisted or folded away.
14#define GLM_ENABLE_EXPERIMENTAL
15#include <glm/glm.hpp>
16#include <glm/gtc/matrix_inverse.hpp> // glm::inverseTranspose
17#include <glm/gtc/type_ptr.hpp> // glm::value_ptr — the flat, column-major buffer
18#include <glm/gtx/norm.hpp> // glm::length2, glm::distance2
20#include <benchmark/benchmark.h>
22#include <cmath>
23#include <cstdio>
24#include <cstdlib>
26#include "fixarray.hpp"
28namespace fa = cheatah::fixarray;
30namespace {
32// ---- deterministic, well-conditioned inputs, identical on both sides ---------------------------
34/// A cheatah Fixed vector/matrix filled the SAME way as gmfill below, so the two sides of every
35/// benchmark operate on identical inputs (the parity check enforces this): element (r, c) is
36/// `base + r*cols + c`, with a diagonal boost for matrices so `inverse` is meaningful. Indexing by
37/// (r, c) — not by flat position — is what keeps it in step with GLM, since the two libraries store
38/// a matrix in opposite orders.
39template <class L>
40L lfill(double base) {
41 L v;
42 using T = typename L::value_type;
43 if constexpr (L::rank == 1) {
44 for (std::size_t i = 0; i < L::size; ++i) { v[i] = static_cast<T>(base + double(i)); }
45 } else {
46 for (std::size_t r = 0; r < L::rows; ++r) {
47 for (std::size_t c = 0; c < L::cols; ++c) {
48 T x = static_cast<T>(base + double(r * L::cols + c));
49 if (r == c) { x += static_cast<T>(10 * L::rows); }
50 v(r, c) = x;
51 }
52 }
53 }
54 return v;
57/// The same entries in a GLM vector.
58template <class G>
59G gvfill(double base) {
60 G v(0);
61 for (int i = 0; i < G::length(); ++i) { v[i] = typename G::value_type(base + double(i)); }
62 return v;
65/// The same entries in a GLM matrix. GLM is column-major, so `m[c][r]` mirrors our `(r, c)`.
66template <class G>
67G gmfill(double base) {
68 G m(1);
69 const int n = G::length();
70 for (int r = 0; r < n; ++r) {
71 for (int c = 0; c < n; ++c) {
72 auto v = typename G::value_type(base + double(r * n + c));
73 if (r == c) { v += typename G::value_type(10 * n); }
74 m[c][r] = v;
75 }
76 }
77 return m;
80// ---- output parity: a benchmark that times the wrong answer is worthless ------------------------
81// Before anything is timed, prove Fixed and GLM compute the SAME result for every operation this
82// file benchmarks. Both store column-major, so a Fixed's data() and GLM's value_ptr line up flat;
83// scalar results (dot, length, det, …) compare directly. On any mismatch the binary aborts with the
84// offending op named, so a benchmark can never quietly compare against a different computation.
86/// True iff the flat buffers agree to a float tolerance (arrays: vectors and matrices alike).
87template <class L, class G>
88bool same_buffer(const L& l, const G& g) {
89 const auto* gp = glm::value_ptr(g);
90 for (std::size_t i = 0; i < L::size; ++i) {
91 if (std::fabs(static_cast<double>(l.data()[i]) - static_cast<double>(gp[i])) > 1e-4) {
92 return false;
93 }
94 }
95 return true;
98/// True iff two scalars agree to a float tolerance.
99inline bool same_scalar(double a, double b) { return std::fabs(a - b) < 1e-4; }
101void abort_if(bool ok, const char* op) {
102 if (!ok) {
103 static_cast<void>(std::fprintf(stderr, "\nfixed_glm_bench: OUTPUT MISMATCH in '%s' — Fixed and GLM disagree.\n",
104 op));
105 std::abort();
106 }
109/// Check one vector op family (L is a Fixed vector, G the matching GLM vector) at the same inputs the
110/// benchmarks use, so the verification and the measurement are of the same computation.
111template <class L, class G>
112void verify_vec() {
113 const L la_a = lfill<L>(1.0), la_b = lfill<L>(2.0);
114 const G g_a = gvfill<G>(1.0), g_b = gvfill<G>(2.0);
115 using T = typename L::value_type;
116 using GT = typename G::value_type;
117 abort_if(same_buffer(la_a + la_b, g_a + g_b), "vec +");
118 abort_if(same_buffer(la_a - la_b, g_a - g_b), "vec -");
119 abort_if(same_buffer(-la_a, -g_a), "vec neg");
120 abort_if(same_buffer(la_a * T(2), g_a * GT(2)), "vec * scalar");
121 abort_if(same_buffer(la_a / T(2), g_a / GT(2)), "vec / scalar");
122 abort_if(same_scalar(fa::dot(la_a, la_b), glm::dot(g_a, g_b)), "dot");
123 abort_if(same_scalar(fa::norm(la_a), glm::length(g_a)), "length");
124 abort_if(same_scalar(fa::squared_norm(la_a), glm::length2(g_a)), "length2");
125 abort_if(same_buffer(fa::normalize(la_a), glm::normalize(g_a)), "normalize");
126 abort_if(same_scalar(fa::distance(la_a, la_b), glm::distance(g_a, g_b)), "distance");
127 abort_if(same_scalar(fa::distance_squared(la_a, la_b), glm::distance2(g_a, g_b)), "distance2");
128 abort_if(same_buffer(fa::reflect(la_a, fa::normalize(la_b)), glm::reflect(g_a, glm::normalize(g_b))),
129 "reflect");
130 abort_if(same_buffer(fa::abs(la_a), glm::abs(g_a)), "abs");
131 abort_if(same_buffer(fa::sign(la_a), glm::sign(g_a)), "sign");
132 abort_if(same_buffer(fa::min(la_a, la_b), glm::min(g_a, g_b)), "min");
133 abort_if(same_buffer(fa::max(la_a, la_b), glm::max(g_a, g_b)), "max");
134 abort_if(same_buffer(fa::clamp(la_a, T(0), T(1)), glm::clamp(g_a, GT(0), GT(1))), "clamp");
135 abort_if(same_buffer(fa::mix(la_a, la_b, T(0.5)), glm::mix(g_a, g_b, GT(0.5))), "mix");
136 abort_if(same_buffer(fa::step(T(1), la_a), glm::step(GT(1), g_a)), "step");
137 abort_if(same_buffer(fa::smoothstep(T(0), T(2), la_a), glm::smoothstep(GT(0), GT(2), g_a)),
138 "smoothstep");
141/// Check one matrix op family (M a Fixed square matrix, G the matching GLM matrix; V/GV the vectors).
142template <class M, class G, class V, class GV>
143void verify_mat() {
144 const M la_a = lfill<M>(1.0), la_b = lfill<M>(2.0);
145 const G g_a = gmfill<G>(1.0), g_b = gmfill<G>(2.0);
146 const V la_v = lfill<V>(1.0);
147 const GV g_v = gvfill<GV>(1.0);
148 using T = typename M::value_type;
149 using GT = typename G::value_type;
150 abort_if(same_buffer(la_a + la_b, g_a + g_b), "mat +");
151 abort_if(same_buffer(la_a * T(2), g_a * GT(2)), "mat * scalar");
152 abort_if(same_buffer(fa::matmul(la_a, la_b), g_a * g_b), "matmul");
153 abort_if(same_buffer(la_a * la_v, g_a * g_v), "mat * vec");
154 abort_if(same_buffer(fa::transpose(la_a), glm::transpose(g_a)), "transpose");
155 abort_if(same_scalar(fa::determinant(la_a), glm::determinant(g_a)), "determinant");
156 abort_if(same_buffer(fa::inverse(la_a), glm::inverse(g_a)), "inverse");
157 abort_if(same_buffer(fa::matrix_comp_mult(la_a, la_b), glm::matrixCompMult(g_a, g_b)),
158 "matrixCompMult");
159 abort_if(same_buffer(fa::outer_product(la_v, la_v), glm::outerProduct(g_v, g_v)), "outerProduct");
160 abort_if(same_buffer(fa::inverse_transpose(la_a), glm::inverseTranspose(g_a)), "inverseTranspose");
161 abort_if(same_buffer(M::identity(), G(1)), "identity");
164/// Runs at static init, before any benchmark: the whole suite verifies against GLM, or aborts.
165const bool kOutputsVerified = [] {
166 verify_vec<fa::vec3f, glm::vec3>();
167 verify_vec<fa::vec4f, glm::vec4>();
168 verify_vec<fa::vec3d, glm::dvec3>();
169 verify_vec<fa::vec4d, glm::dvec4>();
170 verify_mat<fa::mat3f, glm::mat3, fa::vec3f, glm::vec3>();
171 verify_mat<fa::mat4f, glm::mat4, fa::vec4f, glm::vec4>();
172 verify_mat<fa::mat3d, glm::dmat3, fa::vec3d, glm::dvec3>();
173 verify_mat<fa::mat4d, glm::dmat4, fa::vec4d, glm::dvec4>();
174 // cross is 3-D only.
175 abort_if(same_buffer(fa::cross(lfill<fa::vec3f>(1.0), lfill<fa::vec3f>(2.0)),
176 glm::cross(gvfill<glm::vec3>(1.0), gvfill<glm::vec3>(2.0))),
177 "cross");
178 static_cast<void>(std::fprintf(stderr, "fixed_glm_bench: outputs verified against GLM on all benchmarked ops.\n"));
179 return true;
180}();
182// ---- vector operations --------------------------------------------------------------------------
184#define VEC_BENCH(NAME, FIXED_EXPR, GLM_EXPR) \
185 template <class L> \
186 void bm_##NAME##_fixed(benchmark::State& state) { \
187 L a = lfill<L>(1.0), b = lfill<L>(2.0); \
188 for (auto _ : state) { \
189 benchmark::DoNotOptimize(a); \
190 benchmark::DoNotOptimize(b); \
191 auto c = (FIXED_EXPR); \
192 benchmark::DoNotOptimize(&c); \
193 } \
194 } \
195 template <class G> \
196 void bm_##NAME##_glm(benchmark::State& state) { \
197 G a = gvfill<G>(1.0), b = gvfill<G>(2.0); \
198 for (auto _ : state) { \
199 benchmark::DoNotOptimize(a); \
200 benchmark::DoNotOptimize(b); \
201 auto c = (GLM_EXPR); \
202 benchmark::DoNotOptimize(&c); \
203 } \
204 }
206VEC_BENCH(vadd, a + b, a + b)
207VEC_BENCH(vsub, a - b, a - b)
208VEC_BENCH(vneg, -a, -a)
209VEC_BENCH(vmuls, a * typename L::value_type(2), a* typename G::value_type(2))
210VEC_BENCH(vdivs, a / typename L::value_type(2), a / typename G::value_type(2))
211VEC_BENCH(vdot, fa::dot(a, b), glm::dot(a, b))
212VEC_BENCH(vlen, fa::norm(a), glm::length(a))
213VEC_BENCH(vlen2, fa::squared_norm(a), glm::length2(a))
214VEC_BENCH(vnorm, fa::normalize(a), glm::normalize(a))
215VEC_BENCH(vcross, fa::cross(a, b), glm::cross(a, b))
217#define PAIR_VEC_COMMON(L, G, TAG) \
218 BENCHMARK(bm_vadd_fixed<L>)->Name("BM_add_" TAG "_fixed"); \
219 BENCHMARK(bm_vadd_glm<G>)->Name("BM_add_" TAG "_glm"); \
220 BENCHMARK(bm_vsub_fixed<L>)->Name("BM_sub_" TAG "_fixed"); \
221 BENCHMARK(bm_vsub_glm<G>)->Name("BM_sub_" TAG "_glm"); \
222 BENCHMARK(bm_vneg_fixed<L>)->Name("BM_neg_" TAG "_fixed"); \
223 BENCHMARK(bm_vneg_glm<G>)->Name("BM_neg_" TAG "_glm"); \
224 BENCHMARK(bm_vmuls_fixed<L>)->Name("BM_muls_" TAG "_fixed"); \
225 BENCHMARK(bm_vmuls_glm<G>)->Name("BM_muls_" TAG "_glm"); \
226 BENCHMARK(bm_vdivs_fixed<L>)->Name("BM_divs_" TAG "_fixed"); \
227 BENCHMARK(bm_vdivs_glm<G>)->Name("BM_divs_" TAG "_glm"); \
228 BENCHMARK(bm_vdot_fixed<L>)->Name("BM_dot_" TAG "_fixed"); \
229 BENCHMARK(bm_vdot_glm<G>)->Name("BM_dot_" TAG "_glm"); \
230 BENCHMARK(bm_vlen_fixed<L>)->Name("BM_len_" TAG "_fixed"); \
231 BENCHMARK(bm_vlen_glm<G>)->Name("BM_len_" TAG "_glm"); \
232 BENCHMARK(bm_vlen2_fixed<L>)->Name("BM_len2_" TAG "_fixed"); \
233 BENCHMARK(bm_vlen2_glm<G>)->Name("BM_len2_" TAG "_glm"); \
234 BENCHMARK(bm_vnorm_fixed<L>)->Name("BM_normalize_" TAG "_fixed"); \
235 BENCHMARK(bm_vnorm_glm<G>)->Name("BM_normalize_" TAG "_glm");
237PAIR_VEC_COMMON(fa::vec2f, glm::vec2, "vec2f")
238PAIR_VEC_COMMON(fa::vec3f, glm::vec3, "vec3f")
239PAIR_VEC_COMMON(fa::vec4f, glm::vec4, "vec4f")
240PAIR_VEC_COMMON(fa::vec2d, glm::dvec2, "vec2d")
241PAIR_VEC_COMMON(fa::vec3d, glm::dvec3, "vec3d")
242PAIR_VEC_COMMON(fa::vec4d, glm::dvec4, "vec4d")
244BENCHMARK(bm_vcross_fixed<fa::vec3f>)->Name("BM_cross_vec3f_fixed");
245BENCHMARK(bm_vcross_glm<glm::vec3>)->Name("BM_cross_vec3f_glm");
246BENCHMARK(bm_vcross_fixed<fa::vec3d>)->Name("BM_cross_vec3d_fixed");
247BENCHMARK(bm_vcross_glm<glm::dvec3>)->Name("BM_cross_vec3d_glm");
249// ---- matrix operations --------------------------------------------------------------------------
251#define MAT_BENCH(NAME, FIXED_EXPR, GLM_EXPR) \
252 template <class L> \
253 void bm_##NAME##_fixed(benchmark::State& state) { \
254 L a = lfill<L>(1.0), b = lfill<L>(2.0); \
255 for (auto _ : state) { \
256 benchmark::DoNotOptimize(a); \
257 benchmark::DoNotOptimize(b); \
258 auto c = (FIXED_EXPR); \
259 benchmark::DoNotOptimize(&c); \
260 } \
261 } \
262 template <class G> \
263 void bm_##NAME##_glm(benchmark::State& state) { \
264 G a = gmfill<G>(1.0), b = gmfill<G>(2.0); \
265 for (auto _ : state) { \
266 benchmark::DoNotOptimize(a); \
267 benchmark::DoNotOptimize(b); \
268 auto c = (GLM_EXPR); \
269 benchmark::DoNotOptimize(&c); \
270 } \
271 }
273MAT_BENCH(madd, a + b, a + b)
274MAT_BENCH(mmuls, a * typename L::value_type(2), a* typename G::value_type(2))
275MAT_BENCH(mmul, fa::matmul(a, b), a* b)
276MAT_BENCH(mtrans, fa::transpose(a), glm::transpose(a))
277MAT_BENCH(mdet, fa::determinant(a), glm::determinant(a))
278MAT_BENCH(minv, fa::inverse(a), glm::inverse(a))
280/// Matrix * vector, the transform a renderer applies per vertex.
281template <class L, class V>
282void bm_matvec_fixed(benchmark::State& state) {
283 L m = lfill<L>(1.0);
284 V v = lfill<V>(1.0);
285 for (auto _ : state) {
286 benchmark::DoNotOptimize(m);
287 benchmark::DoNotOptimize(v);
288 auto c = m * v;
289 benchmark::DoNotOptimize(&c);
290 }
292template <class G, class GV>
293void bm_matvec_glm(benchmark::State& state) {
294 G m = gmfill<G>(1.0);
295 GV v = gvfill<GV>(1.0);
296 for (auto _ : state) {
297 benchmark::DoNotOptimize(m);
298 benchmark::DoNotOptimize(v);
299 auto c = m * v;
300 benchmark::DoNotOptimize(&c);
301 }
304/// Building the identity — a renderer does this every time it resets a transform.
305template <class L>
306void bm_identity_fixed(benchmark::State& state) {
307 for (auto _ : state) {
308 auto c = L::identity();
309 benchmark::DoNotOptimize(&c);
310 }
312template <class G>
313void bm_identity_glm(benchmark::State& state) {
314 for (auto _ : state) {
315 G c(1);
316 benchmark::DoNotOptimize(&c);
317 }
320#define PAIR_MAT(L, G, V, GV, TAG) \
321 BENCHMARK(bm_madd_fixed<L>)->Name("BM_add_" TAG "_fixed"); \
322 BENCHMARK(bm_madd_glm<G>)->Name("BM_add_" TAG "_glm"); \
323 BENCHMARK(bm_mmuls_fixed<L>)->Name("BM_muls_" TAG "_fixed"); \
324 BENCHMARK(bm_mmuls_glm<G>)->Name("BM_muls_" TAG "_glm"); \
325 BENCHMARK(bm_mmul_fixed<L>)->Name("BM_matmul_" TAG "_fixed"); \
326 BENCHMARK(bm_mmul_glm<G>)->Name("BM_matmul_" TAG "_glm"); \
327 BENCHMARK(bm_mtrans_fixed<L>)->Name("BM_transpose_" TAG "_fixed"); \
328 BENCHMARK(bm_mtrans_glm<G>)->Name("BM_transpose_" TAG "_glm"); \
329 BENCHMARK(bm_mdet_fixed<L>)->Name("BM_det_" TAG "_fixed"); \
330 BENCHMARK(bm_mdet_glm<G>)->Name("BM_det_" TAG "_glm"); \
331 BENCHMARK(bm_minv_fixed<L>)->Name("BM_inverse_" TAG "_fixed"); \
332 BENCHMARK(bm_minv_glm<G>)->Name("BM_inverse_" TAG "_glm"); \
333 BENCHMARK((bm_matvec_fixed<L, V>))->Name("BM_matvec_" TAG "_fixed"); \
334 BENCHMARK((bm_matvec_glm<G, GV>))->Name("BM_matvec_" TAG "_glm"); \
335 BENCHMARK(bm_identity_fixed<L>)->Name("BM_identity_" TAG "_fixed"); \
336 BENCHMARK(bm_identity_glm<G>)->Name("BM_identity_" TAG "_glm");
338PAIR_MAT(fa::mat2f, glm::mat2, fa::vec2f, glm::vec2, "mat2f")
339PAIR_MAT(fa::mat3f, glm::mat3, fa::vec3f, glm::vec3, "mat3f")
340PAIR_MAT(fa::mat4f, glm::mat4, fa::vec4f, glm::vec4, "mat4f")
341PAIR_MAT(fa::mat2d, glm::dmat2, fa::vec2d, glm::dvec2, "mat2d")
342PAIR_MAT(fa::mat3d, glm::dmat3, fa::vec3d, glm::dvec3, "mat3d")
343PAIR_MAT(fa::mat4d, glm::dmat4, fa::vec4d, glm::dvec4, "mat4d")
345// ---- the GLSL/GLM "common" and geometric surface ------------------------------------------------
346// The rest of the overlap: distance/reflect/refract on vectors, and the component-wise builtins.
347// Same DoNotOptimize discipline; VEC_BENCH already fixes the two vector operands a=lfill(1), b=lfill(2)
348// (for GLM, gvfill), so these reuse it. A few need three operands or a scalar, written out here.
350VEC_BENCH(distance, fa::distance(a, b), glm::distance(a, b))
351VEC_BENCH(distance2, fa::distance_squared(a, b), glm::distance2(a, b))
352VEC_BENCH(reflect, fa::reflect(a, fa::normalize(b)), glm::reflect(a, glm::normalize(b)))
353VEC_BENCH(vabs, fa::abs(a), glm::abs(a))
354VEC_BENCH(vsign, fa::sign(a), glm::sign(a))
355VEC_BENCH(vmin, fa::min(a, b), glm::min(a, b))
356VEC_BENCH(vmax, fa::max(a, b), glm::max(a, b))
357VEC_BENCH(vclamp, fa::clamp(a, typename L::value_type(0), typename L::value_type(1)),
358 glm::clamp(a, typename G::value_type(0), typename G::value_type(1)))
359VEC_BENCH(vmix, fa::mix(a, b, typename L::value_type(0.5)),
360 glm::mix(a, b, typename G::value_type(0.5)))
361VEC_BENCH(vstep, fa::step(typename L::value_type(1), a), glm::step(typename G::value_type(1), a))
362VEC_BENCH(vsmoothstep, fa::smoothstep(typename L::value_type(0), typename L::value_type(2), a),
363 glm::smoothstep(typename G::value_type(0), typename G::value_type(2), a))
365#define PAIR_COMMON(L, G, TAG) \
366 BENCHMARK(bm_distance_fixed<L>)->Name("BM_distance_" TAG "_fixed"); \
367 BENCHMARK(bm_distance_glm<G>)->Name("BM_distance_" TAG "_glm"); \
368 BENCHMARK(bm_distance2_fixed<L>)->Name("BM_distance2_" TAG "_fixed"); \
369 BENCHMARK(bm_distance2_glm<G>)->Name("BM_distance2_" TAG "_glm"); \
370 BENCHMARK(bm_reflect_fixed<L>)->Name("BM_reflect_" TAG "_fixed"); \
371 BENCHMARK(bm_reflect_glm<G>)->Name("BM_reflect_" TAG "_glm"); \
372 BENCHMARK(bm_vabs_fixed<L>)->Name("BM_abs_" TAG "_fixed"); \
373 BENCHMARK(bm_vabs_glm<G>)->Name("BM_abs_" TAG "_glm"); \
374 BENCHMARK(bm_vsign_fixed<L>)->Name("BM_sign_" TAG "_fixed"); \
375 BENCHMARK(bm_vsign_glm<G>)->Name("BM_sign_" TAG "_glm"); \
376 BENCHMARK(bm_vmin_fixed<L>)->Name("BM_min_" TAG "_fixed"); \
377 BENCHMARK(bm_vmin_glm<G>)->Name("BM_min_" TAG "_glm"); \
378 BENCHMARK(bm_vmax_fixed<L>)->Name("BM_max_" TAG "_fixed"); \
379 BENCHMARK(bm_vmax_glm<G>)->Name("BM_max_" TAG "_glm"); \
380 BENCHMARK(bm_vclamp_fixed<L>)->Name("BM_clamp_" TAG "_fixed"); \
381 BENCHMARK(bm_vclamp_glm<G>)->Name("BM_clamp_" TAG "_glm"); \
382 BENCHMARK(bm_vmix_fixed<L>)->Name("BM_mix_" TAG "_fixed"); \
383 BENCHMARK(bm_vmix_glm<G>)->Name("BM_mix_" TAG "_glm"); \
384 BENCHMARK(bm_vstep_fixed<L>)->Name("BM_step_" TAG "_fixed"); \
385 BENCHMARK(bm_vstep_glm<G>)->Name("BM_step_" TAG "_glm"); \
386 BENCHMARK(bm_vsmoothstep_fixed<L>)->Name("BM_smoothstep_" TAG "_fixed"); \
387 BENCHMARK(bm_vsmoothstep_glm<G>)->Name("BM_smoothstep_" TAG "_glm");
389PAIR_COMMON(fa::vec3f, glm::vec3, "vec3f")
390PAIR_COMMON(fa::vec4f, glm::vec4, "vec4f")
391PAIR_COMMON(fa::vec3d, glm::dvec3, "vec3d")
392PAIR_COMMON(fa::vec4d, glm::dvec4, "vec4d")
394// matrixCompMult, outerProduct, inverseTranspose — matrix builtins beyond the ordinary product.
395MAT_BENCH(compmult, fa::matrix_comp_mult(a, b), glm::matrixCompMult(a, b))
396MAT_BENCH(invtrans, fa::inverse_transpose(a), glm::inverseTranspose(a))
398template <class L, class G>
399void bm_outer_fixed(benchmark::State& state) {
400 L c = lfill<L>(1.0);
401 L r = lfill<L>(2.0);
402 for (auto _ : state) {
403 benchmark::DoNotOptimize(c);
404 benchmark::DoNotOptimize(r);
405 auto m = fa::outer_product(c, r);
406 benchmark::DoNotOptimize(&m);
407 }
409template <class GV>
410void bm_outer_glm(benchmark::State& state) {
411 GV c = gvfill<GV>(1.0);
412 GV r = gvfill<GV>(2.0);
413 for (auto _ : state) {
414 benchmark::DoNotOptimize(c);
415 benchmark::DoNotOptimize(r);
416 auto m = glm::outerProduct(c, r);
417 benchmark::DoNotOptimize(&m);
418 }
421#define PAIR_MAT_EXTRA(L, G, V, GV, TAG) \
422 BENCHMARK(bm_compmult_fixed<L>)->Name("BM_compmult_" TAG "_fixed"); \
423 BENCHMARK(bm_compmult_glm<G>)->Name("BM_compmult_" TAG "_glm"); \
424 BENCHMARK(bm_invtrans_fixed<L>)->Name("BM_invtrans_" TAG "_fixed"); \
425 BENCHMARK(bm_invtrans_glm<G>)->Name("BM_invtrans_" TAG "_glm"); \
426 BENCHMARK((bm_outer_fixed<V, GV>))->Name("BM_outer_" TAG "_fixed"); \
427 BENCHMARK(bm_outer_glm<GV>)->Name("BM_outer_" TAG "_glm");
429PAIR_MAT_EXTRA(fa::mat3f, glm::mat3, fa::vec3f, glm::vec3, "mat3f")
430PAIR_MAT_EXTRA(fa::mat4f, glm::mat4, fa::vec4f, glm::vec4, "mat4f")
431PAIR_MAT_EXTRA(fa::mat3d, glm::dmat3, fa::vec3d, glm::dvec3, "mat3d")
432PAIR_MAT_EXTRA(fa::mat4d, glm::dmat4, fa::vec4d, glm::dvec4, "mat4d")
434} // namespace