| // Benchmarks for Householder reflections. |
| // |
| // Tests makeHouseholder, makeHouseholderInPlace, applyHouseholderOnTheLeft, |
| // applyHouseholderOnTheRight, HouseholderSequence evaluation, and block |
| // Householder operations. |
| // SPDX-FileCopyrightText: The Eigen Authors |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| #include <benchmark/benchmark.h> |
| #include <Eigen/Householder> |
| #include <Eigen/QR> |
| |
| using namespace Eigen; |
| |
| // ============================================================================= |
| // makeHouseholderInPlace: compute Householder reflector in-place. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_MakeHouseholderInPlace(benchmark::State& state) { |
| const Index n = state.range(0); |
| using Vec = Matrix<Scalar, Dynamic, 1>; |
| using RealScalar = typename NumTraits<Scalar>::Real; |
| |
| Vec v = Vec::Random(n); |
| Vec v_copy = v; |
| Scalar tau; |
| RealScalar beta; |
| for (auto _ : state) { |
| v = v_copy; |
| v.makeHouseholderInPlace(tau, beta); |
| benchmark::DoNotOptimize(tau); |
| benchmark::DoNotOptimize(beta); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // makeHouseholder: compute Householder reflector, storing essential part |
| // separately. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_MakeHouseholder(benchmark::State& state) { |
| const Index n = state.range(0); |
| using Vec = Matrix<Scalar, Dynamic, 1>; |
| using RealScalar = typename NumTraits<Scalar>::Real; |
| |
| Vec v = Vec::Random(n); |
| Vec essential(n - 1); |
| Scalar tau; |
| RealScalar beta; |
| for (auto _ : state) { |
| v.makeHouseholder(essential, tau, beta); |
| benchmark::DoNotOptimize(essential.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // applyHouseholderOnTheLeft: apply H = I - tau * v * v^* from the left. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_ApplyHouseholderOnTheLeft(benchmark::State& state) { |
| const Index rows = state.range(0); |
| const Index cols = state.range(1); |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| using Vec = Matrix<Scalar, Dynamic, 1>; |
| using RealScalar = typename NumTraits<Scalar>::Real; |
| |
| // Create a Householder reflector from a random vector. |
| Vec v = Vec::Random(rows); |
| Vec essential(rows - 1); |
| Scalar tau; |
| RealScalar beta; |
| v.makeHouseholder(essential, tau, beta); |
| |
| Mat A = Mat::Random(rows, cols); |
| Mat A_copy = A; |
| Vec workspace(cols); |
| for (auto _ : state) { |
| A = A_copy; |
| A.applyHouseholderOnTheLeft(essential, tau, workspace.data()); |
| benchmark::DoNotOptimize(A.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // applyHouseholderOnTheRight: apply H = I - tau * v * v^* from the right. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_ApplyHouseholderOnTheRight(benchmark::State& state) { |
| const Index rows = state.range(0); |
| const Index cols = state.range(1); |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| using Vec = Matrix<Scalar, Dynamic, 1>; |
| using RealScalar = typename NumTraits<Scalar>::Real; |
| |
| // Create a Householder reflector from a random vector. |
| Vec v = Vec::Random(cols); |
| Vec essential(cols - 1); |
| Scalar tau; |
| RealScalar beta; |
| v.makeHouseholder(essential, tau, beta); |
| |
| Mat A = Mat::Random(rows, cols); |
| Mat A_copy = A; |
| Vec workspace(rows); |
| for (auto _ : state) { |
| A = A_copy; |
| A.applyHouseholderOnTheRight(essential, tau, workspace.data()); |
| benchmark::DoNotOptimize(A.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // HouseholderSequence evalTo: materialize Q = H_0 * H_1 * ... * H_{k-1} |
| // as a dense matrix. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_HouseholderSequence_EvalTo(benchmark::State& state) { |
| const Index rows = state.range(0); |
| const Index cols = state.range(1); |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| |
| // Build a Householder sequence via QR factorization. |
| Mat A = Mat::Random(rows, cols); |
| HouseholderQR<Mat> qr(A); |
| Mat Q(rows, rows); |
| for (auto _ : state) { |
| Q = qr.householderQ(); |
| benchmark::DoNotOptimize(Q.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // HouseholderSequence applyOnTheLeft: apply Q from the left to a matrix |
| // without materializing Q. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_HouseholderSequence_ApplyLeft(benchmark::State& state) { |
| const Index rows = state.range(0); |
| const Index cols = state.range(1); |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| |
| Mat A = Mat::Random(rows, cols); |
| HouseholderQR<Mat> qr(A); |
| Mat B = Mat::Random(rows, cols); |
| Mat C(rows, cols); |
| for (auto _ : state) { |
| C.noalias() = qr.householderQ() * B; |
| benchmark::DoNotOptimize(C.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // HouseholderSequence applyOnTheRight: apply Q from the right to a matrix |
| // without materializing Q. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_HouseholderSequence_ApplyRight(benchmark::State& state) { |
| const Index rows = state.range(0); |
| const Index cols = state.range(1); |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| |
| Mat A = Mat::Random(rows, cols); |
| HouseholderQR<Mat> qr(A); |
| Mat B = Mat::Random(cols, rows); |
| Mat C(cols, rows); |
| for (auto _ : state) { |
| C.noalias() = B * qr.householderQ(); |
| benchmark::DoNotOptimize(C.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // HouseholderSequence adjoint apply: apply Q^* from the left (common in |
| // least-squares solves: Q^* * b). |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_HouseholderSequence_AdjointApplyLeft(benchmark::State& state) { |
| const Index rows = state.range(0); |
| const Index cols = state.range(1); |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| using Vec = Matrix<Scalar, Dynamic, 1>; |
| |
| Mat A = Mat::Random(rows, cols); |
| HouseholderQR<Mat> qr(A); |
| Vec b = Vec::Random(rows); |
| Vec c(rows); |
| for (auto _ : state) { |
| c.noalias() = qr.householderQ().adjoint() * b; |
| benchmark::DoNotOptimize(c.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // Block Householder: make_block_householder_triangular_factor. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_BlockHouseholder_TriangularFactor(benchmark::State& state) { |
| const Index n = state.range(0); |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| using Vec = Matrix<Scalar, Dynamic, 1>; |
| |
| // Build Householder vectors via QR. |
| Mat A = Mat::Random(n, n); |
| HouseholderQR<Mat> qr(A); |
| const Mat& qrMat = qr.matrixQR(); |
| Vec hCoeffs = qr.hCoeffs(); |
| |
| // Use the full set of vectors. |
| Index nbVecs = (std::min)(n, n); |
| Mat vectors = qrMat.leftCols(nbVecs); |
| Mat T(nbVecs, nbVecs); |
| for (auto _ : state) { |
| internal::make_block_householder_triangular_factor(T, vectors, hCoeffs.head(nbVecs)); |
| benchmark::DoNotOptimize(T.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // Block Householder: apply_block_householder_on_the_left. |
| // ============================================================================= |
| |
| template <typename Scalar> |
| static void BM_BlockHouseholder_ApplyLeft(benchmark::State& state) { |
| const Index n = state.range(0); |
| const Index rhs_cols = state.range(1); |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| using Vec = Matrix<Scalar, Dynamic, 1>; |
| |
| // Build Householder vectors via QR. |
| Mat A = Mat::Random(n, n); |
| HouseholderQR<Mat> qr(A); |
| const Mat& qrMat = qr.matrixQR(); |
| Vec hCoeffs = qr.hCoeffs(); |
| |
| // Use a block of reflectors. |
| Index nbVecs = (std::min)(n, Index(48)); |
| Mat vectors = qrMat.block(0, 0, n, nbVecs); |
| |
| Mat B = Mat::Random(n, rhs_cols); |
| Mat B_copy = B; |
| for (auto _ : state) { |
| B = B_copy; |
| internal::apply_block_householder_on_the_left(B, vectors, hCoeffs.head(nbVecs), true); |
| benchmark::DoNotOptimize(B.data()); |
| } |
| state.SetItemsProcessed(state.iterations()); |
| } |
| |
| // ============================================================================= |
| // Size configurations: chained ->Arg / ->Args macros applied at registration. |
| // ============================================================================= |
| |
| // clang-format off |
| #define VECTOR_SIZES \ |
| ->Arg(8)->Arg(16)->Arg(32)->Arg(64)->Arg(128)->Arg(256)->Arg(512)->Arg(1024)->Arg(4096) |
| |
| #define SQUARE_SIZES \ |
| ->Args({32, 32})->Args({48, 48})->Args({64, 64})->Args({80, 80})->Args({96, 96}) \ |
| ->Args({112, 112})->Args({128, 128})->Args({160, 160})->Args({192, 192})->Args({256, 256}) \ |
| ->Args({384, 384})->Args({512, 512})->Args({768, 768})->Args({1024, 1024}) |
| |
| // Fine-grained sizes around the blocking threshold to find the crossover point. |
| #define SQUARE_SIZES_FINE \ |
| ->Args({32, 32})->Args({40, 40})->Args({48, 48})->Args({56, 56})->Args({64, 64}) \ |
| ->Args({72, 72})->Args({80, 80})->Args({88, 88})->Args({96, 96})->Args({112, 112}) \ |
| ->Args({128, 128})->Args({160, 160})->Args({192, 192})->Args({256, 256}) |
| |
| // Rectangular: many rows, fewer columns (m_length = cols, dst is rows x rows). |
| // Wide dst * narrow Q: dst is (rows x rows), Q is (cols x cols), so rows > cols. |
| #define RECT_APPLY_RIGHT_SIZES \ |
| ->Args({48, 48})->Args({64, 64})->Args({96, 96})->Args({128, 128}) \ |
| ->Args({256, 256})->Args({512, 512})->Args({1024, 1024}) \ |
| ->Args({256, 64})->Args({256, 128}) \ |
| ->Args({512, 64})->Args({512, 128}) \ |
| ->Args({1024, 64})->Args({1024, 128})->Args({1024, 256}) |
| |
| // Square plus tall-thin shapes. |
| #define RECT_SIZES \ |
| ->Args({32, 32})->Args({64, 64})->Args({128, 128}) \ |
| ->Args({256, 256})->Args({512, 512})->Args({1024, 1024}) \ |
| ->Args({1000, 32})->Args({1000, 100})->Args({10000, 32})->Args({10000, 100}) |
| |
| #define BLOCK_SIZES \ |
| ->Args({64, 64})->Args({64, 32}) \ |
| ->Args({128, 128})->Args({128, 32}) \ |
| ->Args({256, 256})->Args({256, 32}) \ |
| ->Args({512, 512})->Args({512, 32}) \ |
| ->Args({1024, 1024})->Args({1024, 32}) |
| // clang-format on |
| |
| // ============================================================================= |
| // Register benchmarks: float |
| // ============================================================================= |
| |
| BENCHMARK(BM_MakeHouseholderInPlace<float>) VECTOR_SIZES->Name("MakeHouseholderInPlace_float"); |
| BENCHMARK(BM_MakeHouseholder<float>) VECTOR_SIZES->Name("MakeHouseholder_float"); |
| BENCHMARK(BM_ApplyHouseholderOnTheLeft<float>) RECT_SIZES->Name("ApplyHouseholderOnTheLeft_float"); |
| BENCHMARK(BM_ApplyHouseholderOnTheRight<float>) RECT_SIZES->Name("ApplyHouseholderOnTheRight_float"); |
| BENCHMARK(BM_HouseholderSequence_EvalTo<float>) SQUARE_SIZES_FINE->Name("HouseholderSequence_EvalTo_float"); |
| BENCHMARK(BM_HouseholderSequence_ApplyLeft<float>) RECT_SIZES->Name("HouseholderSequence_ApplyLeft_float"); |
| BENCHMARK(BM_HouseholderSequence_ApplyRight<float>) |
| RECT_APPLY_RIGHT_SIZES->Name("HouseholderSequence_ApplyRight_float"); |
| BENCHMARK(BM_HouseholderSequence_AdjointApplyLeft<float>) |
| RECT_SIZES->Name("HouseholderSequence_AdjointApplyLeft_float"); |
| BENCHMARK(BM_BlockHouseholder_TriangularFactor<float>) VECTOR_SIZES->Name("BlockHouseholder_TriangularFactor_float"); |
| BENCHMARK(BM_BlockHouseholder_ApplyLeft<float>) BLOCK_SIZES->Name("BlockHouseholder_ApplyLeft_float"); |
| |
| // ============================================================================= |
| // Register benchmarks: double |
| // ============================================================================= |
| |
| BENCHMARK(BM_MakeHouseholderInPlace<double>) VECTOR_SIZES->Name("MakeHouseholderInPlace_double"); |
| BENCHMARK(BM_MakeHouseholder<double>) VECTOR_SIZES->Name("MakeHouseholder_double"); |
| BENCHMARK(BM_ApplyHouseholderOnTheLeft<double>) RECT_SIZES->Name("ApplyHouseholderOnTheLeft_double"); |
| BENCHMARK(BM_ApplyHouseholderOnTheRight<double>) RECT_SIZES->Name("ApplyHouseholderOnTheRight_double"); |
| BENCHMARK(BM_HouseholderSequence_EvalTo<double>) SQUARE_SIZES_FINE->Name("HouseholderSequence_EvalTo_double"); |
| BENCHMARK(BM_HouseholderSequence_ApplyLeft<double>) RECT_SIZES->Name("HouseholderSequence_ApplyLeft_double"); |
| BENCHMARK(BM_HouseholderSequence_ApplyRight<double>) |
| RECT_APPLY_RIGHT_SIZES->Name("HouseholderSequence_ApplyRight_double"); |
| BENCHMARK(BM_HouseholderSequence_AdjointApplyLeft<double>) |
| RECT_SIZES->Name("HouseholderSequence_AdjointApplyLeft_double"); |
| BENCHMARK(BM_BlockHouseholder_TriangularFactor<double>) VECTOR_SIZES->Name("BlockHouseholder_TriangularFactor_double"); |
| BENCHMARK(BM_BlockHouseholder_ApplyLeft<double>) BLOCK_SIZES->Name("BlockHouseholder_ApplyLeft_double"); |
| |
| // ============================================================================= |
| // Register benchmarks: std::complex<double> |
| // ============================================================================= |
| |
| BENCHMARK(BM_MakeHouseholderInPlace<std::complex<double>>) VECTOR_SIZES->Name("MakeHouseholderInPlace_complexdouble"); |
| BENCHMARK(BM_ApplyHouseholderOnTheLeft<std::complex<double>>) |
| RECT_SIZES->Name("ApplyHouseholderOnTheLeft_complexdouble"); |
| BENCHMARK(BM_HouseholderSequence_EvalTo<std::complex<double>>) |
| SQUARE_SIZES->Name("HouseholderSequence_EvalTo_complexdouble"); |
| BENCHMARK(BM_HouseholderSequence_ApplyLeft<std::complex<double>>) |
| SQUARE_SIZES->Name("HouseholderSequence_ApplyLeft_complexdouble"); |