| // Benchmarks for the structured operators' transposition family: transpose(), |
| // conjugate() and adjoint() reuse the operator's cached DFT symbol (an O(n) |
| // permutation / conjugation) instead of recomputing an FFT. The *Recomputed |
| // variants build the same operator through the public constructor, which pays |
| // the FFT, to quantify the reuse. |
| // SPDX-FileCopyrightText: The Eigen Authors |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| #include <benchmark/benchmark.h> |
| #include <Eigen/Core> |
| #include <contrib/Eigen/StructuredMatrices> |
| |
| using namespace Eigen; |
| |
| typedef Matrix<double, Dynamic, 1> Vec; |
| |
| // The generating column of C^T: the index-reversed column of C (what transpose() |
| // computes internally before attaching the reused symbol). |
| static Vec reversedGenerator(const Vec& c) { |
| const Index n = c.size(); |
| Vec rev(n); |
| rev[0] = c[0]; |
| if (n > 1) rev.tail(n - 1) = c.tail(n - 1).reverse(); |
| return rev; |
| } |
| |
| // --- Circulant transpose: construction only --- |
| static void BM_CirculantTransposeCached(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c = Vec::Random(n); |
| Circulant<double> C(c); |
| for (auto _ : state) { |
| Circulant<double> Ct = C.transpose(); // symbol reuse: no FFT |
| benchmark::DoNotOptimize(Ct); |
| } |
| } |
| BENCHMARK(BM_CirculantTransposeCached)->Arg(256)->Arg(1024)->Arg(4096)->Arg(16384); |
| |
| static void BM_CirculantTransposeRecomputed(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c = Vec::Random(n); |
| Vec rev = reversedGenerator(c); |
| for (auto _ : state) { |
| Circulant<double> Ct(rev); // public constructor: recomputes the symbol by FFT |
| benchmark::DoNotOptimize(Ct); |
| } |
| } |
| BENCHMARK(BM_CirculantTransposeRecomputed)->Arg(256)->Arg(1024)->Arg(4096)->Arg(16384); |
| |
| // --- Circulant transpose: end-to-end transposed product y = C^T * x --- |
| static void BM_CirculantTransposeProductCached(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c = Vec::Random(n), x = Vec::Random(n), y(n); |
| Circulant<double> C(c); |
| for (auto _ : state) { |
| y.noalias() = C.transpose() * x; |
| benchmark::DoNotOptimize(y.data()); |
| } |
| } |
| BENCHMARK(BM_CirculantTransposeProductCached)->Arg(256)->Arg(1024)->Arg(4096)->Arg(16384); |
| |
| static void BM_CirculantTransposeProductRecomputed(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c = Vec::Random(n), x = Vec::Random(n), y(n); |
| Vec rev = reversedGenerator(c); |
| for (auto _ : state) { |
| Circulant<double> Ct(rev); |
| y.noalias() = Ct * x; |
| benchmark::DoNotOptimize(y.data()); |
| } |
| } |
| BENCHMARK(BM_CirculantTransposeProductRecomputed)->Arg(256)->Arg(1024)->Arg(4096)->Arg(16384); |
| |
| // --- Toeplitz adjoint: construction only --- |
| static void BM_ToeplitzAdjointCached(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c = Vec::Random(n), r = Vec::Random(n); |
| Toeplitz<double> T(c, r); |
| for (auto _ : state) { |
| Toeplitz<double> Ta = T.adjoint(); // symbol reuse: no FFT |
| benchmark::DoNotOptimize(Ta); |
| } |
| } |
| BENCHMARK(BM_ToeplitzAdjointCached)->Arg(256)->Arg(1024)->Arg(4096)->Arg(16384); |
| |
| static void BM_ToeplitzAdjointRecomputed(benchmark::State& state) { |
| const Index n = state.range(0); |
| Vec c = Vec::Random(n), r = Vec::Random(n); |
| // The adjoint's generators (real scalars, so conjugation is the identity). |
| Vec ca(n); |
| ca[0] = c[0]; |
| if (n > 1) ca.tail(n - 1) = r.tail(n - 1); |
| for (auto _ : state) { |
| Toeplitz<double> Ta(ca, c); // public constructor: recomputes the embedding symbol |
| benchmark::DoNotOptimize(Ta); |
| } |
| } |
| BENCHMARK(BM_ToeplitzAdjointRecomputed)->Arg(256)->Arg(1024)->Arg(4096)->Arg(16384); |