| // Benchmarks for the LookAheadLevinson direct Toeplitz solver: factorization and |
| // cached solves on well-conditioned systems, the look-ahead path on a shifted |
| // KMS family whose leading submatrices are singular (forcing block steps), and |
| // the dense LU alternative. |
| // SPDX-FileCopyrightText: The Eigen Authors |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| #include <benchmark/benchmark.h> |
| #include <Eigen/Dense> |
| #include <contrib/Eigen/StructuredMatrices> |
| |
| using namespace Eigen; |
| |
| typedef Matrix<double, Dynamic, 1> Vec; |
| typedef Matrix<double, Dynamic, Dynamic> Mat; |
| |
| // Diagonally dominant generators: the classical Levinson recursion runs with no |
| // look-ahead blocks. |
| static void wellConditionedToeplitz(Index n, Vec& c, Vec& r) { |
| c = Vec::Random(n); |
| r = Vec::Random(n); |
| r[0] = c[0] += 2.0 * double(n); |
| } |
| |
| // Shifted Kac-Murdock-Szego family: leading submatrices T_k with k = 1, 4, 7, |
| // ... are singular, so every third step is a look-ahead block (n must be a |
| // multiple of 3 for T_n itself to be non-singular). |
| static void shiftedKmsToeplitz(Index n, Vec& c, Vec& r) { |
| c.resize(n); |
| r.resize(n); |
| c[0] = r[0] = 1e-14; |
| for (Index i = 1; i < n; ++i) c[i] = r[i] = std::pow(0.5, double(i - 1)); |
| } |
| |
| // --- Factorization --- |
| static void BM_LevinsonCompute(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c, r; |
| wellConditionedToeplitz(n, c, r); |
| Toeplitz<double> T(c, r); |
| for (auto _ : state) { |
| LookAheadLevinson<double> lev(T); |
| benchmark::DoNotOptimize(lev); |
| } |
| } |
| BENCHMARK(BM_LevinsonCompute)->Arg(32)->Arg(256)->Arg(1024); |
| |
| // --- Solve from a cached factorization --- |
| static void BM_LevinsonSolve(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c, r; |
| wellConditionedToeplitz(n, c, r); |
| Toeplitz<double> T(c, r); |
| LookAheadLevinson<double> lev(T); |
| Vec b = Vec::Random(n); |
| for (auto _ : state) { |
| Vec x = lev.solve(b); |
| benchmark::DoNotOptimize(x.data()); |
| } |
| } |
| BENCHMARK(BM_LevinsonSolve)->Arg(32)->Arg(256)->Arg(1024); |
| |
| // --- Look-ahead path: factorization dominated by block steps --- |
| static void BM_LevinsonComputeLookAhead(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c, r; |
| shiftedKmsToeplitz(n, c, r); |
| Toeplitz<double> T(c, r); |
| for (auto _ : state) { |
| LookAheadLevinson<double> lev; |
| lev.setMaxBlockSize(3).compute(T); |
| benchmark::DoNotOptimize(lev); |
| } |
| } |
| BENCHMARK(BM_LevinsonComputeLookAhead)->Arg(30)->Arg(240)->Arg(1023); |
| |
| // --- Dense alternative: LU factorization + solve on every call --- |
| static void BM_LevinsonSolveDense(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c, r; |
| wellConditionedToeplitz(n, c, r); |
| Mat dense = Toeplitz<double>(c, r); |
| Vec b = Vec::Random(n); |
| for (auto _ : state) { |
| Vec x = PartialPivLU<Mat>(dense).solve(b); |
| benchmark::DoNotOptimize(x.data()); |
| } |
| } |
| BENCHMARK(BM_LevinsonSolveDense)->Arg(256)->Arg(1024); |