| // Benchmarks for the DiagonalPlusLowRank operator: the implicit O(nk) product | |
| // and the O(nk^2 + k^3) Woodbury solve against their dense equivalents (the | |
| // O(n^2) materialized product and the O(n^3) LU factor-and-solve). The solve | |
| // benchmarks time factorization plus one right-hand side on both sides, the | |
| // use case the Woodbury identity accelerates. | |
| // SPDX-FileCopyrightText: The Eigen Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| #include <benchmark/benchmark.h> | |
| #include <Eigen/Core> | |
| #include <Eigen/LU> | |
| #include <contrib/Eigen/StructuredMatrices> | |
| using namespace Eigen; | |
| typedef Matrix<double, Dynamic, 1> Vec; | |
| typedef Matrix<double, Dynamic, Dynamic> Mat; | |
| // Well-conditioned operator: diagonal bounded away from zero and a contractive | |
| // correction, so the capacitance matrix is safely invertible. | |
| static DiagonalPlusLowRank<double> makeOperator(Index n, Index k) { | |
| Vec d = Vec::Random(n).array() + 3.0; | |
| Mat U = 0.5 * Mat::Random(n, k); | |
| Mat V = 0.5 * Mat::Random(n, k); | |
| return DiagonalPlusLowRank<double>(d, U, V); | |
| } | |
| // --- Matrix-vector product: implicit O(nk) vs. materialized dense O(n^2) --- | |
| static void BM_DplrProduct(benchmark::State& state) { | |
| const Index n = state.range(0), k = state.range(1); | |
| DiagonalPlusLowRank<double> A = makeOperator(n, k); | |
| Vec x = Vec::Random(n), y(n); | |
| for (auto _ : state) { | |
| y.noalias() = A * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| } | |
| BENCHMARK(BM_DplrProduct)->ArgsProduct({{256, 1024, 4096}, {4, 32}}); | |
| static void BM_DenseProduct(benchmark::State& state) { | |
| const Index n = state.range(0), k = state.range(1); | |
| Mat dense = makeOperator(n, k); | |
| Vec x = Vec::Random(n), y(n); | |
| for (auto _ : state) { | |
| y.noalias() = dense * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| } | |
| BENCHMARK(BM_DenseProduct)->ArgsProduct({{256, 1024, 4096}, {4, 32}}); | |
| // --- Solve: Woodbury (k x k capacitance LU) vs. dense n x n LU --- | |
| static void BM_DplrSolve(benchmark::State& state) { | |
| const Index n = state.range(0), k = state.range(1); | |
| DiagonalPlusLowRank<double> A = makeOperator(n, k); | |
| Vec b = Vec::Random(n), x(n); | |
| for (auto _ : state) { | |
| x = A.solve(b); // factors the k x k capacitance matrix each iteration | |
| benchmark::DoNotOptimize(x.data()); | |
| } | |
| } | |
| BENCHMARK(BM_DplrSolve)->ArgsProduct({{256, 1024, 4096}, {4, 32}}); | |
| static void BM_DenseLuSolve(benchmark::State& state) { | |
| const Index n = state.range(0), k = state.range(1); | |
| Mat dense = makeOperator(n, k); | |
| Vec b = Vec::Random(n), x(n); | |
| for (auto _ : state) { | |
| x = dense.partialPivLu().solve(b); // factors the n x n matrix each iteration | |
| benchmark::DoNotOptimize(x.data()); | |
| } | |
| } | |
| BENCHMARK(BM_DenseLuSolve)->ArgsProduct({{256, 1024, 4096}, {4, 32}}); |