| // SPDX-FileCopyrightText: The Eigen Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| // Benchmark: SparseMatrix vs BlockSparseMatrix, real and complex scalars. | |
| // | |
| // Naming convention: | |
| // BM_Sm_* — SparseMatrix (standard Eigen sparse) | |
| // BM_BSM_* — BlockSparseMatrix | |
| // *_SpMV — sparse × dense vector (GEMV) | |
| // *_TriMV — triangular-view × dense vector | |
| // *_SymmMV — selfadjoint-view × dense vector | |
| // *_TriSolve — triangular solve in-place | |
| // BM_Sm_Sm_* — SparseMatrix × SparseMatrix | |
| // BM_BSM_BSM_* — BlockSparseMatrix × BlockSparseMatrix | |
| // DiagT — DiagIsTriangular=true (diagonal blocks are actually triangular) | |
| // DiagNSA — DiagIsSelfAdjoint=false with Hermitian diagonal blocks | |
| // DiagSA — DiagIsSelfAdjoint=true with Hermitian diagonal blocks | |
| #include <benchmark/benchmark.h> | |
| #include <Eigen/Sparse> | |
| #include <complex> | |
| #include <random> | |
| #include <set> | |
| using namespace Eigen; | |
| using cd = std::complex<double>; | |
| using cf = std::complex<float>; | |
| // --------------------------------------------------------------------------- | |
| // Decode benchmark args: range(0)=nB, range(1)=sparsity% → nnzPerCol. | |
| // --------------------------------------------------------------------------- | |
| static void parseArgs(const benchmark::State& state, int& nB, int& nnz) { | |
| nB = state.range(0); | |
| nnz = std::max(1, (int)(state.range(1) * nB / 100)); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Random-value helper — works for float, double, complex<float>, complex<double>. | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar> | |
| static typename std::enable_if<std::is_floating_point<Scalar>::value, Scalar>::type randVal( | |
| std::mt19937& rng, std::normal_distribution<double>& d) { | |
| return Scalar(d(rng)); | |
| } | |
| template <typename Scalar> | |
| static typename std::enable_if<!std::is_floating_point<Scalar>::value, Scalar>::type randVal( | |
| std::mt19937& rng, std::normal_distribution<double>& d) { | |
| using R = typename Scalar::value_type; | |
| return Scalar{R(d(rng)), R(d(rng))}; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Build a general (full) block-sparse pair. | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void buildPair(int nB, int nnzPerCol, unsigned seed, BlockSparseMatrix<Scalar, ColMajor, B, B>& bsm, | |
| SparseMatrix<Scalar>& sm) { | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| using BT = typename BSM::BlockType; | |
| using Triplet = typename BSM::TripletType; | |
| std::mt19937 rng(seed); | |
| std::uniform_int_distribution<int> rowDist(0, nB - 1); | |
| std::normal_distribution<double> vd; | |
| std::vector<Triplet> triplets; | |
| triplets.reserve(nB * nnzPerCol); | |
| for (int j = 0; j < nB; ++j) { | |
| std::set<int> rows; | |
| rows.insert(j % nB); | |
| while ((int)rows.size() < std::min(nnzPerCol, nB)) rows.insert(rowDist(rng)); | |
| for (int bi : rows) { | |
| BT blk; | |
| for (int r = 0; r < B; ++r) | |
| for (int c = 0; c < B; ++c) blk(r, c) = randVal<Scalar>(rng, vd); | |
| triplets.emplace_back(bi, j, blk); | |
| } | |
| } | |
| bsm = BSM(nB, nB); | |
| bsm.setFromTriplets(triplets.begin(), triplets.end()); | |
| sm = bsm.toSparse(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Build an upper-triangular block-sparse pair (diagonal blocks are general). | |
| // forSolve=true → diagonal blocks diagonally dominant (well-conditioned). | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void buildUpperTriPair(int nB, int nnzPerCol, unsigned seed, BlockSparseMatrix<Scalar, ColMajor, B, B>& bsm, | |
| SparseMatrix<Scalar>& sm, bool forSolve = false) { | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| using BT = typename BSM::BlockType; | |
| using Triplet = typename BSM::TripletType; | |
| std::mt19937 rng(seed); | |
| std::uniform_int_distribution<int> rowDist(0, nB - 1); | |
| std::normal_distribution<double> vd; | |
| std::vector<Triplet> triplets; | |
| triplets.reserve(nB * nnzPerCol); | |
| for (int j = 0; j < nB; ++j) { | |
| BT diag; | |
| for (int r = 0; r < B; ++r) | |
| for (int c = 0; c < B; ++c) diag(r, c) = randVal<Scalar>(rng, vd); | |
| if (forSolve) { | |
| diag *= Scalar(0.1); | |
| for (int d = 0; d < B; ++d) diag(d, d) += Scalar(double(B)); | |
| } | |
| triplets.emplace_back(j, j, diag); | |
| std::set<int> rows; | |
| while ((int)rows.size() < std::min(nnzPerCol - 1, j)) rows.insert(rowDist(rng) % j); | |
| for (int bi : rows) { | |
| BT blk; | |
| for (int r = 0; r < B; ++r) | |
| for (int c = 0; c < B; ++c) blk(r, c) = randVal<Scalar>(rng, vd); | |
| triplets.emplace_back(bi, j, blk); | |
| } | |
| } | |
| bsm = BSM(nB, nB); | |
| bsm.setFromTriplets(triplets.begin(), triplets.end()); | |
| sm = bsm.toSparse(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Build an upper-triangular pair with actually-triangular diagonal blocks | |
| // (strict lower triangle zeroed). Valid for DiagIsTriangular=true. | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void buildActuallyTriPair(int nB, int nnzPerCol, unsigned seed, BlockSparseMatrix<Scalar, ColMajor, B, B>& bsm, | |
| SparseMatrix<Scalar>& sm) { | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| using BT = typename BSM::BlockType; | |
| using Triplet = typename BSM::TripletType; | |
| std::mt19937 rng(seed); | |
| std::uniform_int_distribution<int> rowDist(0, nB - 1); | |
| std::normal_distribution<double> vd; | |
| std::vector<Triplet> triplets; | |
| triplets.reserve(nB * nnzPerCol); | |
| for (int j = 0; j < nB; ++j) { | |
| BT diag = BT::Zero(); | |
| for (int r = 0; r < B; ++r) | |
| for (int c = r; c < B; ++c) // upper triangle only | |
| diag(r, c) = randVal<Scalar>(rng, vd); | |
| triplets.emplace_back(j, j, diag); | |
| std::set<int> rows; | |
| while ((int)rows.size() < std::min(nnzPerCol - 1, j)) rows.insert(rowDist(rng) % j); | |
| for (int bi : rows) { | |
| BT blk; | |
| for (int r = 0; r < B; ++r) | |
| for (int c = 0; c < B; ++c) blk(r, c) = randVal<Scalar>(rng, vd); | |
| triplets.emplace_back(bi, j, blk); | |
| } | |
| } | |
| bsm = BSM(nB, nB); | |
| bsm.setFromTriplets(triplets.begin(), triplets.end()); | |
| sm = bsm.toSparse(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Build an upper-triangular pair with Hermitian diagonal blocks. | |
| // Valid for DiagIsSelfAdjoint=true. | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void buildHermDiagUpperTriPair(int nB, int nnzPerCol, unsigned seed, | |
| BlockSparseMatrix<Scalar, ColMajor, B, B>& bsm, SparseMatrix<Scalar>& sm) { | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| using BT = typename BSM::BlockType; | |
| using Triplet = typename BSM::TripletType; | |
| std::mt19937 rng(seed); | |
| std::uniform_int_distribution<int> rowDist(0, nB - 1); | |
| std::normal_distribution<double> vd; | |
| std::vector<Triplet> triplets; | |
| triplets.reserve(nB * nnzPerCol); | |
| for (int j = 0; j < nB; ++j) { | |
| BT raw; | |
| for (int r = 0; r < B; ++r) | |
| for (int c = 0; c < B; ++c) raw(r, c) = randVal<Scalar>(rng, vd); | |
| BT diag = (raw + raw.adjoint()) / Scalar(2); | |
| triplets.emplace_back(j, j, diag); | |
| std::set<int> rows; | |
| while ((int)rows.size() < std::min(nnzPerCol - 1, j)) rows.insert(rowDist(rng) % j); | |
| for (int bi : rows) { | |
| BT blk; | |
| for (int r = 0; r < B; ++r) | |
| for (int c = 0; c < B; ++c) blk(r, c) = randVal<Scalar>(rng, vd); | |
| triplets.emplace_back(bi, j, blk); | |
| } | |
| } | |
| bsm = BSM(nB, nB); | |
| bsm.setFromTriplets(triplets.begin(), triplets.end()); | |
| sm = bsm.toSparse(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Sparse×Sparse: Addition | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void BM_Sm_Sm_Add(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> smA, smB, smC; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildPair<Scalar, B>(nB, nnz, 1, tmp, smA); | |
| buildPair<Scalar, B>(nB, nnz, 2, tmp, smB); | |
| for (auto _ : state) { | |
| smC = smA + smB; | |
| benchmark::DoNotOptimize(smC.valuePtr()); | |
| } | |
| state.counters["n"] = smA.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_BSM_Add(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsmA, bsmB, bsmC; | |
| SparseMatrix<Scalar> smTmp; | |
| buildPair<Scalar, B>(nB, nnz, 1, bsmA, smTmp); | |
| buildPair<Scalar, B>(nB, nnz, 2, bsmB, smTmp); | |
| for (auto _ : state) { | |
| bsmC = bsmA + bsmB; | |
| benchmark::DoNotOptimize(bsmC.valuePtr()); | |
| } | |
| state.counters["n"] = bsmA.rows(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Sparse×Dense: GEMV | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void BM_Sm_SpMV(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> sm; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildPair<Scalar, B>(nB, nnz, 1, tmp, sm); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(sm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(sm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = sm * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = sm.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_SpMV(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = bsm * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // Accumulating GEMV: y += A * x. | |
| template <typename Scalar, int B> | |
| static void BM_Sm_SpMV_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> sm; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildPair<Scalar, B>(nB, nnz, 1, tmp, sm); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(sm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(sm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += sm * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = sm.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_SpMV_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += bsm * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Sparse×Dense: Triangular MV | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void BM_Sm_TriMV(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> sm; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, tmp, sm); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(sm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(sm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = sm.template triangularView<Upper>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = sm.rows(); | |
| } | |
| // BSM triangular view, DiagIsTriangular=false: diagonal blocks treated as triangular via triangularView<>. | |
| template <typename Scalar, int B> | |
| static void BM_BSM_TriMV(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = bsm.template triangularView<Upper, false>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // BSM triangular view, DiagIsTriangular=true: diagonal blocks are actually triangular, uses full GEMV. | |
| template <typename Scalar, int B> | |
| static void BM_BSM_TriMV_DiagT(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildActuallyTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = bsm.template triangularView<Upper, true>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // Accumulating triangular MV: y += A * x. | |
| template <typename Scalar, int B> | |
| static void BM_Sm_TriMV_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> sm; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, tmp, sm); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(sm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(sm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += sm.template triangularView<Upper>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = sm.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_TriMV_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += bsm.template triangularView<Upper, false>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_TriMV_DiagT_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildActuallyTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += bsm.template triangularView<Upper, true>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Sparse×Dense: Selfadjoint MV | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void BM_Sm_SymmMV(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> sm; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, tmp, sm); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(sm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(sm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = sm.template selfadjointView<Upper>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = sm.rows(); | |
| } | |
| // BSM selfadjoint view, general (non-Hermitian) diagonal blocks, DiagIsSA=false. | |
| template <typename Scalar, int B> | |
| static void BM_BSM_SymmMV(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = bsm.template selfadjointView<Upper>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // BSM selfadjoint view, Hermitian diagonal blocks, DiagIsSA=false: fills diagonal via selfadjointView<>. | |
| template <typename Scalar, int B> | |
| static void BM_BSM_SymmMV_DiagNSA(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildHermDiagUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = bsm.template selfadjointView<Upper, false>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // BSM selfadjoint view, Hermitian diagonal blocks, DiagIsSA=true: full GEMV for diagonal blocks. | |
| template <typename Scalar, int B> | |
| static void BM_BSM_SymmMV_DiagSA(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildHermDiagUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() = bsm.template selfadjointView<Upper, true>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // Accumulating selfadjoint MV: y += A * x. | |
| template <typename Scalar, int B> | |
| static void BM_Sm_SymmMV_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> sm; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, tmp, sm); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(sm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(sm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += sm.template selfadjointView<Upper>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = sm.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_SymmMV_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += bsm.template selfadjointView<Upper>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_SymmMV_DiagNSA_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildHermDiagUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += bsm.template selfadjointView<Upper, false>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_SymmMV_DiagSA_Acc(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildHermDiagUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp); | |
| Matrix<Scalar, Dynamic, 1> x = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> y = Matrix<Scalar, Dynamic, 1>::Random(bsm.rows()); | |
| for (auto _ : state) { | |
| y.noalias() += bsm.template selfadjointView<Upper, true>() * x; | |
| benchmark::DoNotOptimize(y.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Sparse×Dense: Triangular solve | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void BM_Sm_TriSolve(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> sm; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, tmp, sm, true); | |
| Matrix<Scalar, Dynamic, 1> rhs = Matrix<Scalar, Dynamic, 1>::Random(sm.cols()); | |
| Matrix<Scalar, Dynamic, 1> x(sm.cols()); | |
| for (auto _ : state) { | |
| x = rhs; | |
| sm.template triangularView<Upper>().solveInPlace(x); | |
| benchmark::DoNotOptimize(x.data()); | |
| } | |
| state.counters["n"] = sm.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_TriSolve(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsm; | |
| SparseMatrix<Scalar> smTmp; | |
| buildUpperTriPair<Scalar, B>(nB, nnz, 1, bsm, smTmp, true); | |
| Matrix<Scalar, Dynamic, 1> rhs = Matrix<Scalar, Dynamic, 1>::Random(bsm.cols()); | |
| Matrix<Scalar, Dynamic, 1> x(bsm.cols()); | |
| for (auto _ : state) { | |
| x = rhs; | |
| bsm.template triangularView<Upper>().solveInPlace(x); | |
| benchmark::DoNotOptimize(x.data()); | |
| } | |
| state.counters["n"] = bsm.rows(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Sparse×Sparse: Matrix multiply | |
| // --------------------------------------------------------------------------- | |
| template <typename Scalar, int B> | |
| static void BM_Sm_Sm_Mul(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| SparseMatrix<Scalar> smA, smB, smC; | |
| BlockSparseMatrix<Scalar, ColMajor, B, B> tmp; | |
| buildPair<Scalar, B>(nB, nnz, 1, tmp, smA); | |
| buildPair<Scalar, B>(nB, nnz, 2, tmp, smB); | |
| for (auto _ : state) { | |
| smC = smA * smB; | |
| benchmark::DoNotOptimize(smC.valuePtr()); | |
| } | |
| state.counters["n"] = smA.rows(); | |
| } | |
| template <typename Scalar, int B> | |
| static void BM_BSM_BSM_Mul(benchmark::State& state) { | |
| int nB, nnz; | |
| parseArgs(state, nB, nnz); | |
| using BSM = BlockSparseMatrix<Scalar, ColMajor, B, B>; | |
| BSM bsmA, bsmB, bsmC; | |
| SparseMatrix<Scalar> smTmp; | |
| buildPair<Scalar, B>(nB, nnz, 1, bsmA, smTmp); | |
| buildPair<Scalar, B>(nB, nnz, 2, bsmB, smTmp); | |
| for (auto _ : state) { | |
| bsmC = bsmA * bsmB; | |
| benchmark::DoNotOptimize(bsmC.valuePtr()); | |
| } | |
| state.counters["n"] = bsmA.rows(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Registration | |
| // --------------------------------------------------------------------------- | |
| #define NS benchmark::kNanosecond | |
| #define US benchmark::kMicrosecond | |
| // Args: {nB, sparsity%} — nnzPerCol = max(1, pct*nB/100) | |
| #define REG(fn, S, B) BENCHMARK(fn<S, B>)->Args({200, 1})->Args({200, 5})->Args({200, 10}) | |
| #define BENCH_TYPE(S, B) \ | |
| REG(BM_Sm_Sm_Add, S, B)->Unit(US); \ | |
| REG(BM_BSM_BSM_Add, S, B)->Unit(US); \ | |
| REG(BM_Sm_SpMV, S, B)->Unit(NS); \ | |
| REG(BM_BSM_SpMV, S, B)->Unit(NS); \ | |
| REG(BM_Sm_SpMV_Acc, S, B)->Unit(NS); \ | |
| REG(BM_BSM_SpMV_Acc, S, B)->Unit(NS); \ | |
| REG(BM_Sm_TriMV, S, B)->Unit(NS); \ | |
| REG(BM_BSM_TriMV, S, B)->Unit(NS); \ | |
| REG(BM_BSM_TriMV_DiagT, S, B)->Unit(NS); \ | |
| REG(BM_Sm_TriMV_Acc, S, B)->Unit(NS); \ | |
| REG(BM_BSM_TriMV_Acc, S, B)->Unit(NS); \ | |
| REG(BM_BSM_TriMV_DiagT_Acc, S, B)->Unit(NS); \ | |
| REG(BM_Sm_SymmMV, S, B)->Unit(NS); \ | |
| REG(BM_BSM_SymmMV, S, B)->Unit(NS); \ | |
| REG(BM_BSM_SymmMV_DiagNSA, S, B)->Unit(NS); \ | |
| REG(BM_BSM_SymmMV_DiagSA, S, B)->Unit(NS); \ | |
| REG(BM_Sm_SymmMV_Acc, S, B)->Unit(NS); \ | |
| REG(BM_BSM_SymmMV_Acc, S, B)->Unit(NS); \ | |
| REG(BM_BSM_SymmMV_DiagNSA_Acc, S, B)->Unit(NS); \ | |
| REG(BM_BSM_SymmMV_DiagSA_Acc, S, B)->Unit(NS); \ | |
| REG(BM_Sm_TriSolve, S, B)->Unit(NS); \ | |
| REG(BM_BSM_TriSolve, S, B)->Unit(NS); \ | |
| REG(BM_Sm_Sm_Mul, S, B)->Unit(US); \ | |
| REG(BM_BSM_BSM_Mul, S, B)->Unit(US); | |
| BENCH_TYPE(float, 2) | |
| BENCH_TYPE(cf, 2) | |
| BENCH_TYPE(double, 2) | |
| BENCH_TYPE(cd, 2) | |
| BENCH_TYPE(float, 3) | |
| BENCH_TYPE(cf, 3) | |
| BENCH_TYPE(double, 3) | |
| BENCH_TYPE(cd, 3) | |
| BENCH_TYPE(float, 4) | |
| BENCH_TYPE(cf, 4) | |
| BENCH_TYPE(double, 4) | |
| BENCH_TYPE(cd, 4) |