diff --git a/src/benchmarks/bench_main.cpp b/src/benchmarks/bench_main.cpp new file mode 100644 index 0000000..71fefa0 --- /dev/null +++ b/src/benchmarks/bench_main.cpp @@ -0,0 +1,3 @@ +#include + +BENCHMARK_MAIN(); diff --git a/src/benchmarks/bench_vec4.cpp b/src/benchmarks/bench_vec4.cpp new file mode 100644 index 0000000..365c091 --- /dev/null +++ b/src/benchmarks/bench_vec4.cpp @@ -0,0 +1,135 @@ +#include "tenvex.h" + +#include + +#include +#include +#include + +using namespace tnvx; + +// Runtime data so nothing folds to a compile-time constant. +static std::vector make_vecs(int n, unsigned seed) +{ + std::mt19937 rng(seed); + std::uniform_real_distribution d(-1.0f, 1.0f); + std::vector v; + v.reserve(n); + for (int i = 0; i < n; ++i) + v.push_back(vec4 { d(rng), d(rng), d(rng), 0.0f }); + return v; +} + +// ===================================================================== +// LATENCY: each iteration depends on the previous result (serial chain). +// This is the metric for scattered everyday math: the result is needed +// before moving on. +// ===================================================================== +static void BM_Dot3_Latency(benchmark::State& state) +{ + vec4 a { 1.0f, 2.0f, 3.0f, 0.0f }; + vec4 b { 0.5f, 0.5f, 0.5f, 0.0f }; + float acc = 0.0f; + for (auto _ : state) + { + vec4 bb = b + vec4 { acc, 0.0f, 0.0f, 0.0f }; // depends on previous result + float d = dot3(a, bb); + acc = d * 1e-7f; // feedback = dependency chain + benchmark::DoNotOptimize(acc); + } +} +BENCHMARK(BM_Dot3_Latency); + +static void BM_Norm3_Latency(benchmark::State& state) +{ + vec4 v { 1.0f, 2.0f, 3.0f, 1.0f }; + for (auto _ : state) + { + v = norm3(v) + vec4 { 1e-3f, 2e-3f, 3e-3f, 0.0f }; // chain through v + benchmark::DoNotOptimize(v); + } +} +BENCHMARK(BM_Norm3_Latency); + +// ===================================================================== +// THROUGHPUT: independent inputs, no cross-iteration dependency, so the +// CPU can pipeline. Contrast to latency. +// ===================================================================== +static void BM_Dot3_Throughput(benchmark::State& state) +{ + const auto a = make_vecs(1024, 1); + const auto b = make_vecs(1024, 2); + std::size_t i = 0; + for (auto _ : state) + { + float d = dot3(a[i], b[i]); + benchmark::DoNotOptimize(d); + i = (i + 1) & 1023; + } +} +BENCHMARK(BM_Dot3_Throughput); + +// ===================================================================== +// Compound expression: tenvex vs hand-written intrinsics. +// If the numbers are roughly equal, the abstraction is zero-cost. +// ===================================================================== +static void BM_Compound_tenvex(benchmark::State& state) +{ + const auto va = make_vecs(1024, 1); + const auto vb = make_vecs(1024, 2); + const auto vc = make_vecs(1024, 3); + std::size_t i = 0; + for (auto _ : state) + { + vec4 r = norm3(va[i] + vb[i] * 2.0f) * dot3(vb[i], vc[i]) + vc[i] * 3.0f; + benchmark::DoNotOptimize(r); + i = (i + 1) & 1023; + } +} +BENCHMARK(BM_Compound_tenvex); + +static __m128 dp3_raw(__m128 a, __m128 b) { return _mm_dp_ps(a, b, 0x7F); } + +static __m128 norm3_raw(__m128 v) +{ + __m128 len = _mm_sqrt_ps(dp3_raw(v, v)); + return _mm_blend_ps(_mm_div_ps(v, len), v, 0b1000); +} + +static void BM_Compound_intrinsics(benchmark::State& state) +{ + const auto va = make_vecs(1024, 1); + const auto vb = make_vecs(1024, 2); + const auto vc = make_vecs(1024, 3); + std::size_t i = 0; + for (auto _ : state) + { + __m128 a = va[i].eval(), b = vb[i].eval(), c = vc[i].eval(); + __m128 t = norm3_raw(_mm_add_ps(a, _mm_mul_ps(b, _mm_set1_ps(2.0f)))); + __m128 d = dp3_raw(b, c); + __m128 r = _mm_add_ps(_mm_mul_ps(t, d), _mm_mul_ps(c, _mm_set1_ps(3.0f))); + benchmark::DoNotOptimize(r); + i = (i + 1) & 1023; + } +} +BENCHMARK(BM_Compound_intrinsics); + +// ===================================================================== +// Gameplay-style query: do two units face roughly the same way? +// Latency-bound - the answer is needed before branching on it. +// ===================================================================== +static void BM_FacingSameWay(benchmark::State& state) +{ + vec4 fa = norm3(vec4 { 1.0f, 0.0f, 0.2f, 0.0f }); + vec4 fb = vec4 { 0.9f, 0.1f, 0.1f, 0.0f }; + float jitter = 0.0f; + for (auto _ : state) + { + vec4 f = norm3(fb + vec4 { jitter, 0.0f, 0.0f, 0.0f }); + float c = dot3(fa, f); + bool same = c > 0.95f; + jitter = c * 1e-6f; + benchmark::DoNotOptimize(same); + } +} +BENCHMARK(BM_FacingSameWay);