blob: 0dfbdb7e4b0b361e1be5fa167082f14e9622c866 [file] [edit]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
//
// Micro-benchmark for inverting a symmetric positive definite matrix from its Cholesky factor.
// LLT::inverse() runs the LAPACK POTRI sequence (TRTRI then LAUUM) for 2n^3/3 flops, against the
// 2n^3 of the solve-with-an-explicit-identity form that was the only route before it existed. The
// GFLOPS counters report each routine's own operation count, so they measure utilization rather
// than the speedup; the speedup is the ratio of the wall times.
#include <benchmark/benchmark.h>
#include <Eigen/Cholesky>
#include <Eigen/Core>
using namespace Eigen;
#ifndef SCALAR
#define SCALAR double
#endif
using Scalar = SCALAR;
using MatrixType = Matrix<Scalar, Dynamic, Dynamic>;
using RealScalar = NumTraits<Scalar>::Real;
// Flops counted as in bench_cholesky.cpp: multiply and add counted separately, complex arithmetic
// left at its real-arithmetic count so that the real and complex builds stay comparable.
static double potri_cost(int n) { return 2.0 / 3.0 * double(n) * n * n; }
static double solve_identity_cost(int n) { return 2.0 * double(n) * n * n; }
// TRTRI and LAUUM each cost n^3/3, the two halves of the POTRI sequence above.
static double potri_kernel_cost(int n) { return 1.0 / 3.0 * double(n) * n * n; }
static double trsm_identity_cost(int n) { return double(n) * n * n; }
static MatrixType make_spd(int n) {
MatrixType a = MatrixType::Random(n, n);
return a * a.adjoint() + RealScalar(n) * MatrixType::Identity(n, n);
}
static void set_rate(benchmark::State& state, double flops) {
state.counters["GFLOPS"] =
benchmark::Counter(flops, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
}
static void BM_LLT_Inverse(benchmark::State& state) {
const int n = state.range(0);
const LLT<MatrixType> llt(make_spd(n));
MatrixType inv(n, n);
for (auto _ : state) {
inv = llt.inverse();
benchmark::DoNotOptimize(inv.data());
benchmark::ClobberMemory();
}
set_rate(state, potri_cost(n));
}
BENCHMARK(BM_LLT_Inverse)->RangeMultiplier(2)->Range(8, 2048);
static void BM_LLT_SolveIdentity(benchmark::State& state) {
const int n = state.range(0);
const LLT<MatrixType> llt(make_spd(n));
MatrixType inv(n, n);
for (auto _ : state) {
inv = llt.solve(MatrixType::Identity(n, n));
benchmark::DoNotOptimize(inv.data());
benchmark::ClobberMemory();
}
set_rate(state, solve_identity_cost(n));
}
BENCHMARK(BM_LLT_SolveIdentity)->RangeMultiplier(2)->Range(8, 2048);
// The two kernels LLT::inverse() is built from, and the pre-existing operation each replaces. Both
// pairs carry the same O(n^2) per-iteration setup, so the ratio within a pair is meaningful.
static void BM_TriangularInverseInPlace(benchmark::State& state) {
const int n = state.range(0);
const MatrixType factor = LLT<MatrixType>(make_spd(n)).matrixLLT();
MatrixType x(n, n);
for (auto _ : state) {
x = factor;
x.triangularView<Lower>().inverseInPlace();
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
set_rate(state, potri_kernel_cost(n));
}
BENCHMARK(BM_TriangularInverseInPlace)->RangeMultiplier(2)->Range(8, 2048);
static void BM_TriangularSolveIdentity(benchmark::State& state) {
const int n = state.range(0);
const MatrixType factor = LLT<MatrixType>(make_spd(n)).matrixLLT();
MatrixType x(n, n);
for (auto _ : state) {
x = MatrixType::Identity(n, n);
factor.triangularView<Lower>().solveInPlace(x);
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
set_rate(state, trsm_identity_cost(n));
}
BENCHMARK(BM_TriangularSolveIdentity)->RangeMultiplier(2)->Range(8, 2048);
static void BM_TriangularAdjointSquare(benchmark::State& state) {
const int n = state.range(0);
const MatrixType factor = LLT<MatrixType>(make_spd(n)).matrixLLT();
MatrixType x(n, n);
for (auto _ : state) {
x = factor;
internal::triangular_adjoint_square_in_place<Lower>(x);
benchmark::DoNotOptimize(x.data());
benchmark::ClobberMemory();
}
set_rate(state, potri_kernel_cost(n));
}
BENCHMARK(BM_TriangularAdjointSquare)->RangeMultiplier(2)->Range(8, 2048);