blob: cdbfd2eb28c68a37d2e00a35b86010e510e1532f [file] [log] [blame]
// 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: dmitriy.korchemkin@gmail.com (Dmitriy Korchemkin)
#include <memory>
#include <string>
#include <vector>
#include "benchmark/benchmark.h"
#include "ceres/bundle_adjustment_test_util.h"
#include "ceres/evaluator.h"
#include "ceres/problem.h"
#include "ceres/problem_impl.h"
#include "ceres/program.h"
#include "ceres/sparse_matrix.h"
#include "gflags/gflags.h"
namespace ceres::internal {
// Benchmark library might invoke benchmark function multiple times.
// In order to save time required to parse BAL data, we ensure that
// each dataset is being loaded at most once.
struct BALData {
explicit BALData(const std::string& path) {
bal_problem = std::make_unique<BundleAdjustmentProblem>(path);
CHECK(bal_problem != nullptr);
auto problem_impl = bal_problem->mutable_problem()->mutable_impl();
auto program = problem_impl->mutable_program();
parameters.resize(program->NumParameters());
program->ParameterBlocksToStateVector(parameters.data());
}
Vector parameters;
std::unique_ptr<BundleAdjustmentProblem> bal_problem;
};
static void Residuals(benchmark::State& state,
BALData* data,
ContextImpl* context) {
auto problem = data->bal_problem->mutable_problem();
auto problem_impl = problem->mutable_impl();
CHECK(problem_impl != nullptr);
const int num_threads = state.range(0);
Evaluator::Options options;
options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
options.num_threads = num_threads;
options.context = context;
options.num_eliminate_blocks = 0;
std::string error;
auto program = problem_impl->mutable_program();
auto evaluator = Evaluator::Create(options, program, &error);
CHECK(evaluator != nullptr);
double cost = 0.;
Vector residuals = Vector::Zero(program->NumResiduals());
Evaluator::EvaluateOptions eval_options;
for (auto _ : state) {
CHECK(evaluator->Evaluate(eval_options,
data->parameters.data(),
&cost,
residuals.data(),
nullptr,
nullptr));
}
}
static void ResidualsAndJacobian(benchmark::State& state,
BALData* data,
ContextImpl* context) {
auto problem = data->bal_problem->mutable_problem();
auto problem_impl = problem->mutable_impl();
CHECK(problem_impl != nullptr);
const int num_threads = state.range(0);
Evaluator::Options options;
options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
options.num_threads = num_threads;
options.context = context;
options.num_eliminate_blocks = 0;
std::string error;
auto program = problem_impl->mutable_program();
auto evaluator = Evaluator::Create(options, program, &error);
CHECK(evaluator != nullptr);
double cost = 0.;
Vector residuals = Vector::Zero(program->NumResiduals());
auto jacobian = evaluator->CreateJacobian();
Evaluator::EvaluateOptions eval_options;
for (auto _ : state) {
CHECK(evaluator->Evaluate(eval_options,
data->parameters.data(),
&cost,
residuals.data(),
nullptr,
jacobian.get()));
}
}
} // namespace ceres::internal
// Older versions of benchmark library might come without ::benchmark::Shutdown
// function. We provide an empty fallback variant of Shutdown function in
// order to support both older and newer versions
namespace benchmark_shutdown_fallback {
template <typename... Args>
void Shutdown(Args... args) {}
}; // namespace benchmark_shutdown_fallback
int main(int argc, char** argv) {
::benchmark::Initialize(&argc, argv);
std::vector<std::unique_ptr<ceres::internal::BALData>> benchmark_data;
if (argc == 1) {
LOG(FATAL) << "No input datasets specified. Usage: " << argv[0]
<< " [benchmark flags] path_to_BAL_data_1.txt ... "
"path_to_BAL_data_N.txt";
return -1;
}
ceres::internal::ContextImpl context;
context.EnsureMinimumThreads(16);
for (int i = 1; i < argc; ++i) {
const std::string path(argv[i]);
const std::string name_residuals = "Residuals<" + path + ">";
benchmark_data.emplace_back(
std::make_unique<ceres::internal::BALData>(path));
auto data = benchmark_data.back().get();
::benchmark::RegisterBenchmark(
name_residuals.c_str(), ceres::internal::Residuals, data, &context)
->Arg(1)
->Arg(2)
->Arg(4)
->Arg(8)
->Arg(16);
const std::string name_jacobians = "ResidualsAndJacobian<" + path + ">";
::benchmark::RegisterBenchmark(name_jacobians.c_str(),
ceres::internal::ResidualsAndJacobian,
data,
&context)
->Arg(1)
->Arg(2)
->Arg(4)
->Arg(8)
->Arg(16);
}
::benchmark::RunSpecifiedBenchmarks();
using namespace ::benchmark;
using namespace benchmark_shutdown_fallback;
Shutdown();
return 0;
}