blob: 134dc47d1415132c532ffe447382cc1621cec200 [file]
// Benchmarks for fixed-size products: batches (4x4 transform of 4xN points, arrays of 3x3 products), critical for
// PCL, ROS, Sophus, Drake which use small matrices extensively, and the M x K times K x N shape sweep behind
// EIGEN_FIXED_SIZE_GEMM_TO_COEFFBASED_THRESHOLD (#3119).
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// The *_gemm targets zero every coeff-based bound, so BM_FixedProduct there runs the GEMM path at every shape.
#ifdef EIGEN_BENCH_FORCE_GEMM
#define EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD 2
#define EIGEN_GEMM_TO_COEFFBASED_THRESHOLD 0
#define EIGEN_FIXED_SIZE_GEMM_TO_COEFFBASED_THRESHOLD 0
#define EIGEN_SME_FIXED_SIZE_GEMM_TO_COEFFBASED_THRESHOLD 0
#define EIGEN_SME_GEMM_TO_COEFFBASED_OUTPUT_AREA_THRESHOLD(Scalar) 0
#endif
#include <benchmark/benchmark.h>
#include <Eigen/Core>
using namespace Eigen;
#ifndef SCALAR
#define SCALAR float
#endif
typedef SCALAR Scalar;
#ifdef EIGEN_BENCH_FORCE_GEMM
static_assert(internal::product_type<Matrix<Scalar, 16, 16>, Matrix<Scalar, 16, 16>>::value == GemmProduct,
"the *_gemm targets must not leave any fixed-size product on the coeff-based path");
#endif
using RealScalar = NumTraits<Scalar>::Real;
// A complex multiply-add is four real multiplies and four real adds.
static constexpr double kFlopsPerMulAdd = NumTraits<Scalar>::IsComplex ? 8.0 : 2.0;
#ifndef EIGEN_BENCH_FORCE_GEMM
// --- Batch transform: Matrix4 * Matrix<4,N> ---
static void BM_BatchTransform4xN(benchmark::State& state) {
int N = state.range(0);
typedef Matrix<Scalar, 4, 4> Mat4;
typedef Matrix<Scalar, 4, Dynamic> MatXN;
Mat4 transform = Mat4::Random();
MatXN points = MatXN::Random(4, N);
MatXN result(4, N);
for (auto _ : state) {
result.noalias() = transform * points;
benchmark::DoNotOptimize(result.data());
benchmark::ClobberMemory();
}
state.counters["GFLOPS"] =
benchmark::Counter(2.0 * 4 * 4 * N, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
}
// --- Fixed 3x3 batch operations (common in point cloud processing) ---
static void BM_Batch3x3Gemm(benchmark::State& state) {
int count = state.range(0);
typedef Matrix<Scalar, 3, 3> Mat3;
std::vector<Mat3> a(count), b(count), c(count);
for (int i = 0; i < count; ++i) {
a[i] = Mat3::Random();
b[i] = Mat3::Random();
}
for (auto _ : state) {
for (int i = 0; i < count; ++i) {
c[i].noalias() = a[i] * b[i];
}
benchmark::DoNotOptimize(c.data());
benchmark::ClobberMemory();
}
state.counters["GFLOPS"] =
benchmark::Counter(2.0 * 27 * count, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
}
// Batch 4xN transform
BENCHMARK(BM_BatchTransform4xN)->Arg(1)->Arg(4)->Arg(8)->Arg(16)->Arg(64);
// Batch 3x3 GEMM
BENCHMARK(BM_Batch3x3Gemm)->Arg(100)->Arg(1000)->Arg(10000);
#endif
// --- Fixed-size product dispatch: Matrix<Scalar, M, K> * Matrix<Scalar, K, N> ---
template <int M, int N, int K, bool Lazy>
static void fixed_product(benchmark::State& state) {
using Lhs = Matrix<Scalar, M, K>;
using Rhs = Matrix<Scalar, K, N>;
using Res = Matrix<Scalar, M, N>;
Lhs a = Lhs::Random();
Rhs b = Rhs::Random();
Res c, ref;
ref.noalias() = a.lazyProduct(b);
if (Lazy)
c.noalias() = a.lazyProduct(b);
else
c.noalias() = a * b;
const RealScalar err = (c - ref).norm();
const RealScalar bound = RealScalar(8 * K) * NumTraits<RealScalar>::epsilon() * a.norm() * b.norm();
if (!(err <= bound)) {
state.SkipWithError("product differs from lazyProduct");
return;
}
for (auto _ : state) {
benchmark::DoNotOptimize(a.data());
benchmark::DoNotOptimize(b.data());
if (Lazy)
c.noalias() = a.lazyProduct(b);
else
c.noalias() = a * b;
benchmark::DoNotOptimize(c.data());
benchmark::ClobberMemory();
}
// Compile-time selection only: in an SME build the output-area bound can still reroute a GEMM product at run time.
state.counters["gemm"] = !Lazy && internal::product_type<Lhs, Rhs>::value == GemmProduct;
state.counters["GFLOPS"] = benchmark::Counter(
kFlopsPerMulAdd * M * N * K, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
}
// Takes whichever path this translation unit's thresholds select.
template <int M, int N, int K>
static void BM_FixedProduct(benchmark::State& state) {
fixed_product<M, N, K, false>(state);
}
template <int M, int N, int K>
static void BM_FixedLazyProduct(benchmark::State& state) {
fixed_product<M, N, K, true>(state);
}
// X(M, N, K): square n x n x n, then M = N against K, then rectangular outputs. Every non-square shape has product
// type GemmProduct under the default EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD, so the fixed-size threshold decides it.
// clang-format off
#define EIGEN_BENCH_FIXED_PRODUCT_SHAPES(X) \
X(4, 4, 4) X(5, 5, 5) X(6, 6, 6) X(7, 7, 7) X(8, 8, 8) X(9, 9, 9) X(10, 10, 10) X(11, 11, 11) X(12, 12, 12) \
X(13, 13, 13) X(14, 14, 14) X(15, 15, 15) X(16, 16, 16) X(17, 17, 17) X(18, 18, 18) X(19, 19, 19) X(20, 20, 20) \
X(21, 21, 21) X(22, 22, 22) X(23, 23, 23) X(24, 24, 24) X(25, 25, 25) X(26, 26, 26) X(27, 27, 27) X(28, 28, 28) \
X(29, 29, 29) X(30, 30, 30) X(31, 31, 31) X(32, 32, 32) \
X(2, 2, 8) X(2, 2, 16) X(2, 2, 32) X(2, 2, 64) X(2, 2, 128) \
X(4, 4, 8) X(4, 4, 16) X(4, 4, 32) X(4, 4, 64) X(4, 4, 128) \
X(8, 8, 2) X(8, 8, 4) X(8, 8, 16) X(8, 8, 32) X(8, 8, 64) X(8, 8, 128) \
X(12, 12, 2) X(12, 12, 4) X(12, 12, 8) X(12, 12, 16) X(12, 12, 32) X(12, 12, 64) X(12, 12, 128) \
X(16, 16, 2) X(16, 16, 4) X(16, 16, 8) X(16, 16, 32) X(16, 16, 64) X(16, 16, 128) \
X(24, 24, 2) X(24, 24, 4) X(24, 24, 8) X(24, 24, 16) X(24, 24, 32) X(24, 24, 64) X(24, 24, 128) \
X(32, 32, 2) X(32, 32, 4) X(32, 32, 8) X(32, 32, 16) X(32, 32, 64) X(32, 32, 128) \
X(4, 32, 8) X(4, 32, 16) X(4, 32, 32) X(32, 4, 8) X(32, 4, 16) X(32, 4, 32) \
X(8, 32, 8) X(8, 32, 16) X(8, 32, 32) X(32, 8, 8) X(32, 8, 16) X(32, 8, 32) \
X(2, 64, 16) X(64, 2, 16) X(16, 32, 16) X(32, 16, 16) \
X(2, 2, 256) X(2, 2, 512) X(4, 4, 256) X(4, 4, 512) \
X(8, 8, 20) X(8, 8, 24) X(8, 8, 28) X(16, 16, 20) X(16, 16, 24) X(16, 16, 28) \
X(32, 32, 20) X(32, 32, 24) X(32, 32, 28) \
X(48, 48, 2) X(48, 48, 4) X(48, 48, 8) X(48, 48, 16) X(48, 48, 32) \
X(64, 64, 2) X(64, 64, 4) X(64, 64, 8) X(64, 64, 16) X(64, 64, 32)
// clang-format on
#define EIGEN_BENCH_REGISTER_FIXED_PRODUCT(M, N, K) \
BENCHMARK_TEMPLATE(BM_FixedProduct, M, N, K); \
BENCHMARK_TEMPLATE(BM_FixedLazyProduct, M, N, K);
EIGEN_BENCH_FIXED_PRODUCT_SHAPES(EIGEN_BENCH_REGISTER_FIXED_PRODUCT)