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