Add benchmarks for unsupported modules and extend supported benchmarks

libeigen/eigen!2179

Closes #3036

Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
This commit is contained in:
Rasmus Munk Larsen
2026-02-24 17:12:33 -08:00
parent fa567f6bcd
commit 16da0279f1
33 changed files with 2320 additions and 10 deletions

View File

@@ -1 +1,2 @@
eigen_add_benchmark(bench_cholesky bench_cholesky.cpp)
eigen_add_benchmark(bench_cholesky_double bench_cholesky.cpp DEFINITIONS SCALAR=double)

View File

@@ -4,7 +4,11 @@
using namespace Eigen;
typedef float Scalar;
#ifndef SCALAR
#define SCALAR float
#endif
typedef SCALAR Scalar;
static void BM_LDLT(benchmark::State& state) {
int n = state.range(0);

View File

@@ -15,3 +15,5 @@ eigen_add_benchmark(bench_diagonal bench_diagonal.cpp)
eigen_add_benchmark(bench_triangular_product bench_triangular_product.cpp)
eigen_add_benchmark(bench_selfadjoint_product bench_selfadjoint_product.cpp)
eigen_add_benchmark(bench_construction bench_construction.cpp)
eigen_add_benchmark(bench_fixed_size bench_fixed_size.cpp)
eigen_add_benchmark(bench_fixed_size_double bench_fixed_size.cpp DEFINITIONS SCALAR=double)

View File

@@ -0,0 +1,123 @@
// Benchmarks for fixed-size matrix operations (2x2, 3x3, 4x4).
// Critical for PCL, ROS, Sophus, Drake which use small matrices extensively.
#include <benchmark/benchmark.h>
#include <Eigen/Core>
#include <Eigen/LU>
using namespace Eigen;
#ifndef SCALAR
#define SCALAR float
#endif
typedef SCALAR Scalar;
// --- Fixed-size GEMM ---
template <int N>
static void BM_FixedGemm(benchmark::State& state) {
typedef Matrix<Scalar, N, N> Mat;
Mat a = Mat::Random();
Mat b = Mat::Random();
Mat c;
for (auto _ : state) {
c.noalias() = a * b;
benchmark::DoNotOptimize(c.data());
benchmark::ClobberMemory();
}
state.counters["GFLOPS"] =
benchmark::Counter(2.0 * N * N * N, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
}
// --- Fixed-size inverse ---
template <int N>
static void BM_FixedInverse(benchmark::State& state) {
typedef Matrix<Scalar, N, N> Mat;
Mat a = Mat::Random();
// Make well-conditioned.
a = a * a.transpose() + Mat::Identity();
Mat result;
for (auto _ : state) {
result = a.inverse();
benchmark::DoNotOptimize(result.data());
benchmark::ClobberMemory();
}
}
// --- Fixed-size determinant ---
template <int N>
static void BM_FixedDeterminant(benchmark::State& state) {
typedef Matrix<Scalar, N, N> Mat;
Mat a = Mat::Random();
Scalar result;
for (auto _ : state) {
result = a.determinant();
benchmark::DoNotOptimize(&result);
benchmark::ClobberMemory();
}
}
// --- 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);
}
// Fixed-size GEMM
BENCHMARK(BM_FixedGemm<2>)->Name("FixedGemm_2x2");
BENCHMARK(BM_FixedGemm<3>)->Name("FixedGemm_3x3");
BENCHMARK(BM_FixedGemm<4>)->Name("FixedGemm_4x4");
// Fixed-size inverse
BENCHMARK(BM_FixedInverse<2>)->Name("FixedInverse_2x2");
BENCHMARK(BM_FixedInverse<3>)->Name("FixedInverse_3x3");
BENCHMARK(BM_FixedInverse<4>)->Name("FixedInverse_4x4");
// Fixed-size determinant
BENCHMARK(BM_FixedDeterminant<2>)->Name("FixedDet_2x2");
BENCHMARK(BM_FixedDeterminant<3>)->Name("FixedDet_3x3");
BENCHMARK(BM_FixedDeterminant<4>)->Name("FixedDet_4x4");
// 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);

View File

@@ -1,2 +1,3 @@
eigen_add_benchmark(bench_eigensolver bench_eigensolver.cpp)
eigen_add_benchmark(bench_eigensolver_double bench_eigensolver.cpp DEFINITIONS SCALAR=double)
eigen_add_benchmark(bench_eig33 bench_eig33.cpp)

View File

@@ -5,7 +5,11 @@
using namespace Eigen;
typedef float Scalar;
#ifndef SCALAR
#define SCALAR float
#endif
typedef SCALAR Scalar;
static void BM_SelfAdjointEigenSolver(benchmark::State& state) {
int n = state.range(0);

View File

@@ -33,11 +33,20 @@ static void BM_FFT(benchmark::State& state) {
benchmark::Counter(mflops_per_iter, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
}
BENCHMARK(BM_FFT<std::complex<float>, true>)->Arg(1024)->Arg(4096);
BENCHMARK(BM_FFT<std::complex<float>, false>)->Arg(1024)->Arg(4096);
BENCHMARK(BM_FFT<float, true>)->Arg(1024)->Arg(4096);
BENCHMARK(BM_FFT<float, false>)->Arg(1024)->Arg(4096);
BENCHMARK(BM_FFT<std::complex<double>, true>)->Arg(1024)->Arg(4096);
BENCHMARK(BM_FFT<std::complex<double>, false>)->Arg(1024)->Arg(4096);
BENCHMARK(BM_FFT<double, true>)->Arg(1024)->Arg(4096);
BENCHMARK(BM_FFT<double, false>)->Arg(1024)->Arg(4096);
static void FFTSizes(::benchmark::Benchmark* b) {
for (int n : {64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 65536}) {
b->Arg(n);
}
// Non-power-of-2 sizes.
b->Arg(1000);
b->Arg(5000);
}
BENCHMARK(BM_FFT<std::complex<float>, true>)->Apply(FFTSizes);
BENCHMARK(BM_FFT<std::complex<float>, false>)->Apply(FFTSizes);
BENCHMARK(BM_FFT<float, true>)->Apply(FFTSizes);
BENCHMARK(BM_FFT<float, false>)->Apply(FFTSizes);
BENCHMARK(BM_FFT<std::complex<double>, true>)->Apply(FFTSizes);
BENCHMARK(BM_FFT<std::complex<double>, false>)->Apply(FFTSizes);
BENCHMARK(BM_FFT<double, true>)->Apply(FFTSizes);
BENCHMARK(BM_FFT<double, false>)->Apply(FFTSizes);

View File

@@ -1,3 +1,4 @@
eigen_add_benchmark(bench_spmv bench_spmv.cpp)
eigen_add_benchmark(bench_spmm bench_spmm.cpp)
eigen_add_benchmark(bench_sparse_transpose bench_sparse_transpose.cpp)
eigen_add_benchmark(bench_sparse_solvers bench_sparse_solvers.cpp)

View File

@@ -0,0 +1,182 @@
// Benchmarks for sparse decomposition solvers.
// Tests SimplicialLLT, SimplicialLDLT, SparseQR, SparseLU, CG, BiCGSTAB.
#include <benchmark/benchmark.h>
#include <Eigen/Sparse>
#include <Eigen/SparseCholesky>
#include <Eigen/SparseLU>
#include <Eigen/SparseQR>
#include <Eigen/IterativeLinearSolvers>
#include <Eigen/OrderingMethods>
using namespace Eigen;
typedef double Scalar;
typedef SparseMatrix<Scalar> SpMat;
typedef Matrix<Scalar, Dynamic, 1> Vec;
// Generate a SPD banded matrix (Laplacian-like).
static SpMat generateSPD(int n, int bandwidth) {
SpMat A(n, n);
std::vector<Triplet<Scalar>> trips;
trips.reserve(n * (2 * bandwidth + 1));
for (int i = 0; i < n; ++i) {
Scalar diag = 0;
for (int j = std::max(0, i - bandwidth); j < std::min(n, i + bandwidth + 1); ++j) {
if (i != j) {
Scalar val = -1.0 / (1 + std::abs(i - j));
trips.emplace_back(i, j, val);
diag -= val;
}
}
trips.emplace_back(i, i, diag + 1.0);
}
A.setFromTriplets(trips.begin(), trips.end());
return A;
}
// Generate a general (non-symmetric) sparse matrix with diagonal dominance.
static SpMat generateGeneral(int n, int bandwidth) {
SpMat A(n, n);
std::vector<Triplet<Scalar>> trips;
trips.reserve(n * (2 * bandwidth + 1));
for (int i = 0; i < n; ++i) {
Scalar diag = 0;
for (int j = std::max(0, i - bandwidth); j < std::min(n, i + bandwidth + 1); ++j) {
if (i != j) {
Scalar val = -0.5 / (1 + std::abs(i - j));
if (j > i) val *= 1.5;
trips.emplace_back(i, j, val);
diag += std::abs(val);
}
}
trips.emplace_back(i, i, diag + 1.0);
}
A.setFromTriplets(trips.begin(), trips.end());
return A;
}
// --- SimplicialLLT ---
static void BM_SimplicialLLT(benchmark::State& state) {
int n = state.range(0);
int bw = state.range(1);
SpMat A = generateSPD(n, bw);
Vec b = Vec::Random(n);
for (auto _ : state) {
SimplicialLLT<SpMat> solver(A);
Vec x = solver.solve(b);
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
}
// --- SimplicialLDLT ---
static void BM_SimplicialLDLT(benchmark::State& state) {
int n = state.range(0);
int bw = state.range(1);
SpMat A = generateSPD(n, bw);
Vec b = Vec::Random(n);
for (auto _ : state) {
SimplicialLDLT<SpMat> solver(A);
Vec x = solver.solve(b);
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
}
// --- SparseLU ---
static void BM_SparseLU(benchmark::State& state) {
int n = state.range(0);
int bw = state.range(1);
SpMat A = generateGeneral(n, bw);
Vec b = Vec::Random(n);
for (auto _ : state) {
SparseLU<SpMat, COLAMDOrdering<int>> solver;
solver.compute(A);
Vec x = solver.solve(b);
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
}
// --- SparseQR ---
static void BM_SparseQR(benchmark::State& state) {
int n = state.range(0);
int bw = state.range(1);
SpMat A = generateGeneral(n, bw);
Vec b = Vec::Random(n);
for (auto _ : state) {
SparseQR<SpMat, COLAMDOrdering<int>> solver;
solver.compute(A);
Vec x = solver.solve(b);
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
}
// --- ConjugateGradient (SPD) ---
static void BM_CG(benchmark::State& state) {
int n = state.range(0);
int bw = state.range(1);
SpMat A = generateSPD(n, bw);
Vec b = Vec::Random(n);
ConjugateGradient<SpMat> solver;
solver.setMaxIterations(1000);
solver.setTolerance(1e-10);
solver.compute(A);
for (auto _ : state) {
Vec x = solver.solve(b);
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
state.counters["iterations"] = solver.iterations();
}
// --- BiCGSTAB (general) ---
static void BM_BiCGSTAB(benchmark::State& state) {
int n = state.range(0);
int bw = state.range(1);
SpMat A = generateGeneral(n, bw);
Vec b = Vec::Random(n);
BiCGSTAB<SpMat> solver;
solver.setMaxIterations(1000);
solver.setTolerance(1e-10);
solver.compute(A);
for (auto _ : state) {
Vec x = solver.solve(b);
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
state.counters["iterations"] = solver.iterations();
}
static void DirectSolverSizes(::benchmark::Benchmark* b) {
for (int n : {1000, 5000, 10000, 50000}) {
for (int bw : {5, 20}) {
b->Args({n, bw});
}
}
}
static void IterativeSolverSizes(::benchmark::Benchmark* b) {
for (int n : {1000, 10000, 50000}) {
for (int bw : {5, 20}) {
b->Args({n, bw});
}
}
}
BENCHMARK(BM_SimplicialLLT)->Apply(DirectSolverSizes);
BENCHMARK(BM_SimplicialLDLT)->Apply(DirectSolverSizes);
BENCHMARK(BM_SparseLU)->Apply(DirectSolverSizes);
BENCHMARK(BM_SparseQR)->Apply(DirectSolverSizes);
BENCHMARK(BM_CG)->Apply(IterativeSolverSizes);
BENCHMARK(BM_BiCGSTAB)->Apply(IterativeSolverSizes);