blob: 069659b0cfe30b1fc8aec2af6941d057365fd6ac [file]
// 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);