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 both5
// precisions. glm_compare_bench.cpp measures the dynamic NDArray on GLM's home turf (and loses on6
// 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 a10
// regression anywhere is a single line to read. Both sides compile with the same flags in the same11
// 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_EXPERIMENTAL15
#include <glm/glm.hpp>16
#include <glm/gtc/matrix_inverse.hpp> // glm::inverseTranspose17
#include <glm/gtc/type_ptr.hpp> // glm::value_ptr — the flat, column-major buffer18
#include <glm/gtx/norm.hpp> // glm::length2, glm::distance220
#include <benchmark/benchmark.h>22
#include <cmath>23
#include <cstdio>24
#include <cstdlib>26
#include "fixarray.hpp"28
namespace fa = cheatah::fixarray;30
namespace {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 every35
/// benchmark operate on identical inputs (the parity check enforces this): element (r, c) is36
/// `base + r*cols + c`, with a diagonal boost for matrices so `inverse` is meaningful. Indexing by37
/// (r, c) — not by flat position — is what keeps it in step with GLM, since the two libraries store38
/// a matrix in opposite orders.39
template <class L>40
L 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;55
}57
/// The same entries in a GLM vector.58
template <class G>59
G 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;63
}65
/// The same entries in a GLM matrix. GLM is column-major, so `m[c][r]` mirrors our `(r, c)`.66
template <class G>67
G 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;78
}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 this82
// 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 the84
// 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).87
template <class L, class G>88
bool 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;96
}98
/// True iff two scalars agree to a float tolerance.99
inline bool same_scalar(double a, double b) { return std::fabs(a - b) < 1e-4; }101
void 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
}107
}109
/// Check one vector op family (L is a Fixed vector, G the matching GLM vector) at the same inputs the110
/// benchmarks use, so the verification and the measurement are of the same computation.111
template <class L, class G>112
void 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");139
}141
/// Check one matrix op family (M a Fixed square matrix, G the matching GLM matrix; V/GV the vectors).142
template <class M, class G, class V, class GV>143
void 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");162
}164
/// Runs at static init, before any benchmark: the whole suite verifies against GLM, or aborts.165
const 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
}206
VEC_BENCH(vadd, a + b, a + b)207
VEC_BENCH(vsub, a - b, a - b)208
VEC_BENCH(vneg, -a, -a)209
VEC_BENCH(vmuls, a * typename L::value_type(2), a* typename G::value_type(2))210
VEC_BENCH(vdivs, a / typename L::value_type(2), a / typename G::value_type(2))211
VEC_BENCH(vdot, fa::dot(a, b), glm::dot(a, b))212
VEC_BENCH(vlen, fa::norm(a), glm::length(a))213
VEC_BENCH(vlen2, fa::squared_norm(a), glm::length2(a))214
VEC_BENCH(vnorm, fa::normalize(a), glm::normalize(a))215
VEC_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");237
PAIR_VEC_COMMON(fa::vec2f, glm::vec2, "vec2f")238
PAIR_VEC_COMMON(fa::vec3f, glm::vec3, "vec3f")239
PAIR_VEC_COMMON(fa::vec4f, glm::vec4, "vec4f")240
PAIR_VEC_COMMON(fa::vec2d, glm::dvec2, "vec2d")241
PAIR_VEC_COMMON(fa::vec3d, glm::dvec3, "vec3d")242
PAIR_VEC_COMMON(fa::vec4d, glm::dvec4, "vec4d")244
BENCHMARK(bm_vcross_fixed<fa::vec3f>)->Name("BM_cross_vec3f_fixed");245
BENCHMARK(bm_vcross_glm<glm::vec3>)->Name("BM_cross_vec3f_glm");246
BENCHMARK(bm_vcross_fixed<fa::vec3d>)->Name("BM_cross_vec3d_fixed");247
BENCHMARK(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
}273
MAT_BENCH(madd, a + b, a + b)274
MAT_BENCH(mmuls, a * typename L::value_type(2), a* typename G::value_type(2))275
MAT_BENCH(mmul, fa::matmul(a, b), a* b)276
MAT_BENCH(mtrans, fa::transpose(a), glm::transpose(a))277
MAT_BENCH(mdet, fa::determinant(a), glm::determinant(a))278
MAT_BENCH(minv, fa::inverse(a), glm::inverse(a))280
/// Matrix * vector, the transform a renderer applies per vertex.281
template <class L, class V>282
void 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
}291
}292
template <class G, class GV>293
void 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
}302
}304
/// Building the identity — a renderer does this every time it resets a transform.305
template <class L>306
void bm_identity_fixed(benchmark::State& state) {307
for (auto _ : state) {308
auto c = L::identity();309
benchmark::DoNotOptimize(&c);310
}311
}312
template <class G>313
void bm_identity_glm(benchmark::State& state) {314
for (auto _ : state) {315
G c(1);316
benchmark::DoNotOptimize(&c);317
}318
}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");338
PAIR_MAT(fa::mat2f, glm::mat2, fa::vec2f, glm::vec2, "mat2f")339
PAIR_MAT(fa::mat3f, glm::mat3, fa::vec3f, glm::vec3, "mat3f")340
PAIR_MAT(fa::mat4f, glm::mat4, fa::vec4f, glm::vec4, "mat4f")341
PAIR_MAT(fa::mat2d, glm::dmat2, fa::vec2d, glm::dvec2, "mat2d")342
PAIR_MAT(fa::mat3d, glm::dmat3, fa::vec3d, glm::dvec3, "mat3d")343
PAIR_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.350
VEC_BENCH(distance, fa::distance(a, b), glm::distance(a, b))351
VEC_BENCH(distance2, fa::distance_squared(a, b), glm::distance2(a, b))352
VEC_BENCH(reflect, fa::reflect(a, fa::normalize(b)), glm::reflect(a, glm::normalize(b)))353
VEC_BENCH(vabs, fa::abs(a), glm::abs(a))354
VEC_BENCH(vsign, fa::sign(a), glm::sign(a))355
VEC_BENCH(vmin, fa::min(a, b), glm::min(a, b))356
VEC_BENCH(vmax, fa::max(a, b), glm::max(a, b))357
VEC_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)))359
VEC_BENCH(vmix, fa::mix(a, b, typename L::value_type(0.5)),360
glm::mix(a, b, typename G::value_type(0.5)))361
VEC_BENCH(vstep, fa::step(typename L::value_type(1), a), glm::step(typename G::value_type(1), a))362
VEC_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");389
PAIR_COMMON(fa::vec3f, glm::vec3, "vec3f")390
PAIR_COMMON(fa::vec4f, glm::vec4, "vec4f")391
PAIR_COMMON(fa::vec3d, glm::dvec3, "vec3d")392
PAIR_COMMON(fa::vec4d, glm::dvec4, "vec4d")394
// matrixCompMult, outerProduct, inverseTranspose — matrix builtins beyond the ordinary product.395
MAT_BENCH(compmult, fa::matrix_comp_mult(a, b), glm::matrixCompMult(a, b))396
MAT_BENCH(invtrans, fa::inverse_transpose(a), glm::inverseTranspose(a))398
template <class L, class G>399
void 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
}408
}409
template <class GV>410
void 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
}419
}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");429
PAIR_MAT_EXTRA(fa::mat3f, glm::mat3, fa::vec3f, glm::vec3, "mat3f")430
PAIR_MAT_EXTRA(fa::mat4f, glm::mat4, fa::vec4f, glm::vec4, "mat4f")431
PAIR_MAT_EXTRA(fa::mat3d, glm::dmat3, fa::vec3d, glm::dvec3, "mat3d")432
PAIR_MAT_EXTRA(fa::mat4d, glm::dmat4, fa::vec4d, glm::dvec4, "mat4d")434
} // namespace