|  | // Ceres Solver - A fast non-linear least squares minimizer | 
|  | // Copyright 2022 Google Inc. All rights reserved. | 
|  | // http://ceres-solver.org/ | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are met: | 
|  | // | 
|  | // * Redistributions of source code must retain the above copyright notice, | 
|  | //   this list of conditions and the following disclaimer. | 
|  | // * Redistributions in binary form must reproduce the above copyright notice, | 
|  | //   this list of conditions and the following disclaimer in the documentation | 
|  | //   and/or other materials provided with the distribution. | 
|  | // * Neither the name of Google Inc. nor the names of its contributors may be | 
|  | //   used to endorse or promote products derived from this software without | 
|  | //   specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | 
|  | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
|  | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
|  | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
|  | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
|  | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
|  | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
|  | // POSSIBILITY OF SUCH DAMAGE. | 
|  | // | 
|  | // Authors: sameeragarwal@google.com (Sameer Agarwal) | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <random> | 
|  | #include <vector> | 
|  |  | 
|  | #include "Eigen/Dense" | 
|  | #include "benchmark/benchmark.h" | 
|  | #include "ceres/block_random_access_dense_matrix.h" | 
|  | #include "ceres/block_sparse_matrix.h" | 
|  | #include "ceres/block_structure.h" | 
|  | #include "ceres/schur_eliminator.h" | 
|  |  | 
|  | namespace ceres::internal { | 
|  |  | 
|  | constexpr int kRowBlockSize = 2; | 
|  | constexpr int kEBlockSize = 3; | 
|  | constexpr int kFBlockSize = 6; | 
|  |  | 
|  | class BenchmarkData { | 
|  | public: | 
|  | explicit BenchmarkData(const int num_e_blocks) { | 
|  | auto* bs = new CompressedRowBlockStructure; | 
|  | bs->cols.resize(num_e_blocks + 1); | 
|  | int col_pos = 0; | 
|  | for (int i = 0; i < num_e_blocks; ++i) { | 
|  | bs->cols[i].position = col_pos; | 
|  | bs->cols[i].size = kEBlockSize; | 
|  | col_pos += kEBlockSize; | 
|  | } | 
|  | bs->cols.back().position = col_pos; | 
|  | bs->cols.back().size = kFBlockSize; | 
|  |  | 
|  | bs->rows.resize(2 * num_e_blocks); | 
|  | int row_pos = 0; | 
|  | int cell_pos = 0; | 
|  | for (int i = 0; i < num_e_blocks; ++i) { | 
|  | { | 
|  | auto& row = bs->rows[2 * i]; | 
|  | row.block.position = row_pos; | 
|  | row.block.size = kRowBlockSize; | 
|  | row_pos += kRowBlockSize; | 
|  | auto& cells = row.cells; | 
|  | cells.resize(2); | 
|  | cells[0].block_id = i; | 
|  | cells[0].position = cell_pos; | 
|  | cell_pos += kRowBlockSize * kEBlockSize; | 
|  | cells[1].block_id = num_e_blocks; | 
|  | cells[1].position = cell_pos; | 
|  | cell_pos += kRowBlockSize * kFBlockSize; | 
|  | } | 
|  | { | 
|  | auto& row = bs->rows[2 * i + 1]; | 
|  | row.block.position = row_pos; | 
|  | row.block.size = kRowBlockSize; | 
|  | row_pos += kRowBlockSize; | 
|  | auto& cells = row.cells; | 
|  | cells.resize(1); | 
|  | cells[0].block_id = i; | 
|  | cells[0].position = cell_pos; | 
|  | cell_pos += kRowBlockSize * kEBlockSize; | 
|  | } | 
|  | } | 
|  |  | 
|  | matrix_ = std::make_unique<BlockSparseMatrix>(bs); | 
|  | double* values = matrix_->mutable_values(); | 
|  | std::generate_n(values, matrix_->num_nonzeros(), [this] { | 
|  | return standard_normal_(prng_); | 
|  | }); | 
|  |  | 
|  | b_.resize(matrix_->num_rows()); | 
|  | b_.setRandom(); | 
|  |  | 
|  | std::vector<Block> blocks; | 
|  | blocks.emplace_back(kFBlockSize, 0); | 
|  | lhs_ = std::make_unique<BlockRandomAccessDenseMatrix>(blocks, &context_, 1); | 
|  | diagonal_.resize(matrix_->num_cols()); | 
|  | diagonal_.setOnes(); | 
|  | rhs_.resize(kFBlockSize); | 
|  |  | 
|  | y_.resize(num_e_blocks * kEBlockSize); | 
|  | y_.setZero(); | 
|  | z_.resize(kFBlockSize); | 
|  | z_.setOnes(); | 
|  | } | 
|  |  | 
|  | const BlockSparseMatrix& matrix() const { return *matrix_; } | 
|  | const Vector& b() const { return b_; } | 
|  | const Vector& diagonal() const { return diagonal_; } | 
|  | BlockRandomAccessDenseMatrix* mutable_lhs() { return lhs_.get(); } | 
|  | Vector* mutable_rhs() { return &rhs_; } | 
|  | Vector* mutable_y() { return &y_; } | 
|  | Vector* mutable_z() { return &z_; } | 
|  |  | 
|  | ContextImpl* context() { return &context_; } | 
|  |  | 
|  | private: | 
|  | ContextImpl context_; | 
|  |  | 
|  | std::unique_ptr<BlockSparseMatrix> matrix_; | 
|  | Vector b_; | 
|  | std::unique_ptr<BlockRandomAccessDenseMatrix> lhs_; | 
|  | Vector rhs_; | 
|  | Vector diagonal_; | 
|  | Vector z_; | 
|  | Vector y_; | 
|  | std::mt19937 prng_; | 
|  | std::normal_distribution<> standard_normal_; | 
|  | }; | 
|  |  | 
|  | static void BM_SchurEliminatorEliminate(benchmark::State& state) { | 
|  | const int num_e_blocks = state.range(0); | 
|  | BenchmarkData data(num_e_blocks); | 
|  |  | 
|  | LinearSolver::Options linear_solver_options; | 
|  | linear_solver_options.e_block_size = kEBlockSize; | 
|  | linear_solver_options.row_block_size = kRowBlockSize; | 
|  | linear_solver_options.f_block_size = kFBlockSize; | 
|  | linear_solver_options.context = data.context(); | 
|  | std::unique_ptr<SchurEliminatorBase> eliminator( | 
|  | SchurEliminatorBase::Create(linear_solver_options)); | 
|  |  | 
|  | eliminator->Init(num_e_blocks, true, data.matrix().block_structure()); | 
|  | for (auto _ : state) { | 
|  | eliminator->Eliminate(BlockSparseMatrixData(data.matrix()), | 
|  | data.b().data(), | 
|  | data.diagonal().data(), | 
|  | data.mutable_lhs(), | 
|  | data.mutable_rhs()->data()); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void BM_SchurEliminatorBackSubstitute(benchmark::State& state) { | 
|  | const int num_e_blocks = state.range(0); | 
|  | BenchmarkData data(num_e_blocks); | 
|  |  | 
|  | LinearSolver::Options linear_solver_options; | 
|  | linear_solver_options.e_block_size = kEBlockSize; | 
|  | linear_solver_options.row_block_size = kRowBlockSize; | 
|  | linear_solver_options.f_block_size = kFBlockSize; | 
|  | linear_solver_options.context = data.context(); | 
|  | std::unique_ptr<SchurEliminatorBase> eliminator( | 
|  | SchurEliminatorBase::Create(linear_solver_options)); | 
|  |  | 
|  | eliminator->Init(num_e_blocks, true, data.matrix().block_structure()); | 
|  | eliminator->Eliminate(BlockSparseMatrixData(data.matrix()), | 
|  | data.b().data(), | 
|  | data.diagonal().data(), | 
|  | data.mutable_lhs(), | 
|  | data.mutable_rhs()->data()); | 
|  | for (auto _ : state) { | 
|  | eliminator->BackSubstitute(BlockSparseMatrixData(data.matrix()), | 
|  | data.b().data(), | 
|  | data.diagonal().data(), | 
|  | data.mutable_z()->data(), | 
|  | data.mutable_y()->data()); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void BM_SchurEliminatorForOneFBlockEliminate(benchmark::State& state) { | 
|  | const int num_e_blocks = state.range(0); | 
|  | BenchmarkData data(num_e_blocks); | 
|  | SchurEliminatorForOneFBlock<2, 3, 6> eliminator; | 
|  | eliminator.Init(num_e_blocks, true, data.matrix().block_structure()); | 
|  | for (auto _ : state) { | 
|  | eliminator.Eliminate(BlockSparseMatrixData(data.matrix()), | 
|  | data.b().data(), | 
|  | data.diagonal().data(), | 
|  | data.mutable_lhs(), | 
|  | data.mutable_rhs()->data()); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void BM_SchurEliminatorForOneFBlockBackSubstitute( | 
|  | benchmark::State& state) { | 
|  | const int num_e_blocks = state.range(0); | 
|  | BenchmarkData data(num_e_blocks); | 
|  | SchurEliminatorForOneFBlock<2, 3, 6> eliminator; | 
|  | eliminator.Init(num_e_blocks, true, data.matrix().block_structure()); | 
|  | eliminator.Eliminate(BlockSparseMatrixData(data.matrix()), | 
|  | data.b().data(), | 
|  | data.diagonal().data(), | 
|  | data.mutable_lhs(), | 
|  | data.mutable_rhs()->data()); | 
|  | for (auto _ : state) { | 
|  | eliminator.BackSubstitute(BlockSparseMatrixData(data.matrix()), | 
|  | data.b().data(), | 
|  | data.diagonal().data(), | 
|  | data.mutable_z()->data(), | 
|  | data.mutable_y()->data()); | 
|  | } | 
|  | } | 
|  |  | 
|  | BENCHMARK(BM_SchurEliminatorEliminate)->Range(10, 10000); | 
|  | BENCHMARK(BM_SchurEliminatorForOneFBlockEliminate)->Range(10, 10000); | 
|  | BENCHMARK(BM_SchurEliminatorBackSubstitute)->Range(10, 10000); | 
|  | BENCHMARK(BM_SchurEliminatorForOneFBlockBackSubstitute)->Range(10, 10000); | 
|  |  | 
|  | }  // namespace ceres::internal | 
|  |  | 
|  | BENCHMARK_MAIN(); |