// Benchmarks for Householder reflections. // // Tests makeHouseholder, makeHouseholderInPlace, applyHouseholderOnTheLeft, // applyHouseholderOnTheRight, HouseholderSequence evaluation, and block // Householder operations. #include #include #include using namespace Eigen; // ============================================================================= // makeHouseholderInPlace: compute Householder reflector in-place. // ============================================================================= template static void BM_MakeHouseholderInPlace(benchmark::State& state) { const Index n = state.range(0); using Vec = Matrix; using RealScalar = typename NumTraits::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 static void BM_MakeHouseholder(benchmark::State& state) { const Index n = state.range(0); using Vec = Matrix; using RealScalar = typename NumTraits::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 static void BM_ApplyHouseholderOnTheLeft(benchmark::State& state) { const Index rows = state.range(0); const Index cols = state.range(1); using Mat = Matrix; using Vec = Matrix; using RealScalar = typename NumTraits::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 static void BM_ApplyHouseholderOnTheRight(benchmark::State& state) { const Index rows = state.range(0); const Index cols = state.range(1); using Mat = Matrix; using Vec = Matrix; using RealScalar = typename NumTraits::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 static void BM_HouseholderSequence_EvalTo(benchmark::State& state) { const Index rows = state.range(0); const Index cols = state.range(1); using Mat = Matrix; // Build a Householder sequence via QR factorization. Mat A = Mat::Random(rows, cols); HouseholderQR 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 static void BM_HouseholderSequence_ApplyLeft(benchmark::State& state) { const Index rows = state.range(0); const Index cols = state.range(1); using Mat = Matrix; Mat A = Mat::Random(rows, cols); HouseholderQR 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 static void BM_HouseholderSequence_ApplyRight(benchmark::State& state) { const Index rows = state.range(0); const Index cols = state.range(1); using Mat = Matrix; Mat A = Mat::Random(rows, cols); HouseholderQR 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 static void BM_HouseholderSequence_AdjointApplyLeft(benchmark::State& state) { const Index rows = state.range(0); const Index cols = state.range(1); using Mat = Matrix; using Vec = Matrix; Mat A = Mat::Random(rows, cols); HouseholderQR 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 static void BM_BlockHouseholder_TriangularFactor(benchmark::State& state) { const Index n = state.range(0); using Mat = Matrix; using Vec = Matrix; // Build Householder vectors via QR. Mat A = Mat::Random(n, n); HouseholderQR 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 static void BM_BlockHouseholder_ApplyLeft(benchmark::State& state) { const Index n = state.range(0); const Index rhs_cols = state.range(1); using Mat = Matrix; using Vec = Matrix; // Build Householder vectors via QR. Mat A = Mat::Random(n, n); HouseholderQR 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 // ============================================================================= static void VectorSizes(::benchmark::Benchmark* b) { for (int n : {8, 16, 32, 64, 128, 256, 512, 1024, 4096}) b->Arg(n); } static void SquareSizes(::benchmark::Benchmark* b) { for (int n : {32, 48, 64, 80, 96, 112, 128, 160, 192, 256, 384, 512, 768, 1024}) b->Args({n, n}); } // Fine-grained sizes around the blocking threshold to find the crossover point. static void SquareSizesFine(::benchmark::Benchmark* b) { for (int n : {32, 40, 48, 56, 64, 72, 80, 88, 96, 112, 128, 160, 192, 256}) b->Args({n, n}); } // Rectangular: many rows, fewer columns (m_length = cols, dst is rows x rows). static void RectApplyRight(::benchmark::Benchmark* b) { // Square for (int n : {48, 64, 96, 128, 256, 512, 1024}) b->Args({n, n}); // Wide dst * narrow Q: dst is (rows x rows), Q is (cols x cols), so rows > cols. b->Args({256, 64}); b->Args({256, 128}); b->Args({512, 64}); b->Args({512, 128}); b->Args({1024, 64}); b->Args({1024, 128}); b->Args({1024, 256}); } static void RectSizes(::benchmark::Benchmark* b) { // Square for (int n : {32, 64, 128, 256, 512, 1024}) b->Args({n, n}); // Tall-thin b->Args({1000, 32}); b->Args({1000, 100}); b->Args({10000, 32}); b->Args({10000, 100}); } static void BlockSizes(::benchmark::Benchmark* b) { for (int n : {64, 128, 256, 512, 1024}) { b->Args({n, n}); b->Args({n, 32}); } } // ============================================================================= // Register benchmarks: float // ============================================================================= BENCHMARK(BM_MakeHouseholderInPlace)->Apply(VectorSizes)->Name("MakeHouseholderInPlace_float"); BENCHMARK(BM_MakeHouseholder)->Apply(VectorSizes)->Name("MakeHouseholder_float"); BENCHMARK(BM_ApplyHouseholderOnTheLeft)->Apply(RectSizes)->Name("ApplyHouseholderOnTheLeft_float"); BENCHMARK(BM_ApplyHouseholderOnTheRight)->Apply(RectSizes)->Name("ApplyHouseholderOnTheRight_float"); BENCHMARK(BM_HouseholderSequence_EvalTo)->Apply(SquareSizesFine)->Name("HouseholderSequence_EvalTo_float"); BENCHMARK(BM_HouseholderSequence_ApplyLeft)->Apply(RectSizes)->Name("HouseholderSequence_ApplyLeft_float"); BENCHMARK(BM_HouseholderSequence_ApplyRight) ->Apply(RectApplyRight) ->Name("HouseholderSequence_ApplyRight_float"); BENCHMARK(BM_HouseholderSequence_AdjointApplyLeft) ->Apply(RectSizes) ->Name("HouseholderSequence_AdjointApplyLeft_float"); BENCHMARK(BM_BlockHouseholder_TriangularFactor) ->Apply(VectorSizes) ->Name("BlockHouseholder_TriangularFactor_float"); BENCHMARK(BM_BlockHouseholder_ApplyLeft)->Apply(BlockSizes)->Name("BlockHouseholder_ApplyLeft_float"); // ============================================================================= // Register benchmarks: double // ============================================================================= BENCHMARK(BM_MakeHouseholderInPlace)->Apply(VectorSizes)->Name("MakeHouseholderInPlace_double"); BENCHMARK(BM_MakeHouseholder)->Apply(VectorSizes)->Name("MakeHouseholder_double"); BENCHMARK(BM_ApplyHouseholderOnTheLeft)->Apply(RectSizes)->Name("ApplyHouseholderOnTheLeft_double"); BENCHMARK(BM_ApplyHouseholderOnTheRight)->Apply(RectSizes)->Name("ApplyHouseholderOnTheRight_double"); BENCHMARK(BM_HouseholderSequence_EvalTo)->Apply(SquareSizesFine)->Name("HouseholderSequence_EvalTo_double"); BENCHMARK(BM_HouseholderSequence_ApplyLeft)->Apply(RectSizes)->Name("HouseholderSequence_ApplyLeft_double"); BENCHMARK(BM_HouseholderSequence_ApplyRight) ->Apply(RectApplyRight) ->Name("HouseholderSequence_ApplyRight_double"); BENCHMARK(BM_HouseholderSequence_AdjointApplyLeft) ->Apply(RectSizes) ->Name("HouseholderSequence_AdjointApplyLeft_double"); BENCHMARK(BM_BlockHouseholder_TriangularFactor) ->Apply(VectorSizes) ->Name("BlockHouseholder_TriangularFactor_double"); BENCHMARK(BM_BlockHouseholder_ApplyLeft)->Apply(BlockSizes)->Name("BlockHouseholder_ApplyLeft_double"); // ============================================================================= // Register benchmarks: std::complex // ============================================================================= BENCHMARK(BM_MakeHouseholderInPlace>) ->Apply(VectorSizes) ->Name("MakeHouseholderInPlace_complexdouble"); BENCHMARK(BM_ApplyHouseholderOnTheLeft>) ->Apply(RectSizes) ->Name("ApplyHouseholderOnTheLeft_complexdouble"); BENCHMARK(BM_HouseholderSequence_EvalTo>) ->Apply(SquareSizes) ->Name("HouseholderSequence_EvalTo_complexdouble"); BENCHMARK(BM_HouseholderSequence_ApplyLeft>) ->Apply(SquareSizes) ->Name("HouseholderSequence_ApplyLeft_complexdouble");