| // Benchmarks for the Bccb operator: the O(N log N) 2-D-FFT-based product and | |
| // direct solve against their dense counterparts (GEMV, and a solve with a | |
| // precomputed PartialPivLU factorization -- the analogue of Bccb's symbol, | |
| // which is precomputed at construction). The generating array is n x n, so the | |
| // operator is N x N with N = n^2; the dense variants stop at n = 64 (N = 4096) | |
| // to keep the dense matrix and its factorization affordable. | |
| // 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; | |
| // Diagonally dominant generating array: keeps the symbol away from zero, so the | |
| // solves are well conditioned. | |
| static Mat generatingArray(Index n) { | |
| Mat G = Mat::Random(n, n); | |
| G(0, 0) += double(2 * n * n); | |
| return G; | |
| } | |
| // The dense representation, built entry-wise: entry (i,j) with i = b1*n2 + i2, | |
| // j = c1*n2 + j2 is G((i2-j2) mod n2, (b1-c1) mod n1). | |
| static Mat denseBccb(const Mat& G) { | |
| const Index n2 = G.rows(), n1 = G.cols(), N = n1 * n2; | |
| Mat dense(N, N); | |
| for (Index j = 0; j < N; ++j) | |
| for (Index i = 0; i < N; ++i) { | |
| Index k2 = i % n2 - j % n2; | |
| if (k2 < 0) k2 += n2; | |
| Index k1 = i / n2 - j / n2; | |
| if (k1 < 0) k1 += n1; | |
| dense(i, j) = G(k2, k1); | |
| } | |
| return dense; | |
| } | |
| // --- product: y = C * x --- | |
| static void BM_BccbProduct(benchmark::State& state) { | |
| const Index n = state.range(0), N = n * n; | |
| Mat G = generatingArray(n); | |
| Bccb<double> C(G); | |
| Vec x = Vec::Random(N), y(N); | |
| for (auto _ : state) { | |
| y.noalias() = C * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| } | |
| // 97 is prime in both block dimensions: the product pins the padded embedding | |
| // (the exact-size transform would run kissfft's quadratic generic butterfly). | |
| BENCHMARK(BM_BccbProduct)->Arg(8)->Arg(16)->Arg(32)->Arg(64)->Arg(97)->Arg(128); | |
| static void BM_DenseProduct(benchmark::State& state) { | |
| const Index n = state.range(0), N = n * n; | |
| Mat dense = denseBccb(generatingArray(n)); | |
| Vec x = Vec::Random(N), y(N); | |
| for (auto _ : state) { | |
| y.noalias() = dense * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| } | |
| BENCHMARK(BM_DenseProduct)->Arg(8)->Arg(16)->Arg(32)->Arg(64); | |
| // --- solve: x = C^{-1} * b, both sides reusing their precomputed factorization | |
| // (the 2-D DFT symbol for Bccb, the LU factors for the dense matrix) --- | |
| static void BM_BccbSolve(benchmark::State& state) { | |
| const Index n = state.range(0), N = n * n; | |
| Mat G = generatingArray(n); | |
| Bccb<double> C(G); | |
| Vec b = Vec::Random(N), x(N); | |
| for (auto _ : state) { | |
| x = C.solve(b); | |
| benchmark::DoNotOptimize(x.data()); | |
| } | |
| } | |
| // All 5-smooth: solve transforms at the exact size, so a prime dimension would | |
| // only time kissfft's generic butterfly instead of the operator. | |
| BENCHMARK(BM_BccbSolve)->Arg(8)->Arg(16)->Arg(32)->Arg(64)->Arg(96)->Arg(128); | |
| static void BM_DenseSolve(benchmark::State& state) { | |
| const Index n = state.range(0), N = n * n; | |
| Mat dense = denseBccb(generatingArray(n)); | |
| PartialPivLU<Mat> lu(dense); | |
| Vec b = Vec::Random(N), x(N); | |
| for (auto _ : state) { | |
| x = lu.solve(b); | |
| benchmark::DoNotOptimize(x.data()); | |
| } | |
| } | |
| BENCHMARK(BM_DenseSolve)->Arg(8)->Arg(16)->Arg(32)->Arg(64); |