| // 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); |