Add Codegen BA Benchmark

Add a benchmark that compares codegen-autodiff versus old autodiff
on the snavely reprojection error cost function. This creates two
new cmake targets:

codegen_ba_benchmark
codegen_ba_benchark_fast_math

The second one is only enabled on Clang and includes all known
optimizations.

Change-Id: Ibc30dc8b67b4c0fece41142914f9118f16ff1e20
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index 9e64b89..a8813a6 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -506,6 +506,7 @@
   target_link_libraries(${BENCHMARK_TARGET} PUBLIC ceres benchmark::benchmark)
   target_include_directories(${BENCHMARK_TARGET} PUBLIC
                              ${Ceres_SOURCE_DIR}/internal
+                             ${Ceres_SOURCE_DIR}/internal/ceres
                              ${CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS})
 endmacro()
 
diff --git a/internal/ceres/codegen/CMakeLists.txt b/internal/ceres/codegen/CMakeLists.txt
index 4bea21f..4dd75ed 100644
--- a/internal/ceres/codegen/CMakeLists.txt
+++ b/internal/ceres/codegen/CMakeLists.txt
@@ -88,3 +88,31 @@
     UnaryArithmetic BinaryComparison LogicalOperators ScalarFunctions
     LogicalFunctions Branches)
 endif()
+
+if (BUILD_BENCHMARKS)
+  generate_test_functor(SnavelyReprojectionErrorGen snavely_reprojection_error.h)
+
+  # Benchmark with default compiler flags
+  add_executable(codegen_ba_benchmark codegen_ba_benchmark.cc)
+  add_dependencies_to_benchmark(codegen_ba_benchmark)
+  target_link_libraries(codegen_ba_benchmark PUBLIC SnavelyReprojectionErrorGen)
+
+  # Benchmark with all optimizations (clang only)
+  if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+
+    # Always fine
+    list(APPEND CERES_BENCHMARK_FLAGS "-march=native")
+    list(APPEND CERES_BENCHMARK_FLAGS "-fno-math-errno" "-fno-trapping-math" "-fassociative-math" "-fno-signed-zeros" "-ffp-contract=fast")
+
+    # This enables the 0*x=0 optimization
+    list(APPEND CERES_BENCHMARK_FLAGS "-fno-honor-infinities" "-fno-honor-nans")
+
+    # Total overkill but we want to make sure everything inlineable is inlined.
+    list(APPEND CERES_BENCHMARK_FLAGS "-mllvm" "-inline-threshold=1000000")
+
+    add_executable(codegen_ba_benchmark_fast_math codegen_ba_benchmark.cc)
+    add_dependencies_to_benchmark(codegen_ba_benchmark_fast_math)
+    target_link_libraries(codegen_ba_benchmark_fast_math PUBLIC SnavelyReprojectionErrorGen)
+    target_compile_options(codegen_ba_benchmark_fast_math PRIVATE ${CERES_BENCHMARK_FLAGS})
+  endif()
+endif()
diff --git a/internal/ceres/codegen/codegen_ba_benchmark.cc b/internal/ceres/codegen/codegen_ba_benchmark.cc
new file mode 100644
index 0000000..8d37d75
--- /dev/null
+++ b/internal/ceres/codegen/codegen_ba_benchmark.cc
@@ -0,0 +1,94 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2018 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)
+//#define EIGEN_DONT_VECTORIZE
+#include <memory>
+
+#include "benchmark/benchmark.h"
+#include "ceres/ceres.h"
+#include "snavely_reprojection_error.h"
+#include "test_utils.h"
+
+namespace ceres {
+
+static void BM_BAAutoDiff(benchmark::State& state) {
+  using FunctorType =
+      ceres::internal::CostFunctionToFunctor<test::SnavelyReprojectionErrorGen>;
+
+  double parameter_block1[] = {1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double parameter_block2[] = {1., 2., 3.};
+  double* parameters[] = {parameter_block1, parameter_block2};
+
+  double jacobian1[2 * 9];
+  double jacobian2[2 * 3];
+  double residuals[2];
+  double* jacobians[] = {jacobian1, jacobian2};
+
+  const double x = 0.2;
+  const double y = 0.3;
+  std::unique_ptr<ceres::CostFunction> cost_function(
+      new ceres::AutoDiffCostFunction<FunctorType, 2, 9, 3>(
+          new FunctorType(x, y)));
+
+  while (state.KeepRunning()) {
+    cost_function->Evaluate(
+        parameters, residuals, state.range(0) ? jacobians : nullptr);
+  }
+}
+
+static void BM_BACodeGen(benchmark::State& state) {
+  double parameter_block1[] = {1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double parameter_block2[] = {1., 2., 3.};
+  double* parameters[] = {parameter_block1, parameter_block2};
+
+  double jacobian1[2 * 9];
+  double jacobian2[2 * 3];
+  double residuals[2];
+  double* jacobians[] = {jacobian1, jacobian2};
+
+  const double x = 0.2;
+  const double y = 0.3;
+
+  std::unique_ptr<ceres::CostFunction> cost_function(
+      new test::SnavelyReprojectionErrorGen(x, y));
+
+  while (state.KeepRunning()) {
+    cost_function->Evaluate(
+        parameters, residuals, state.range(0) ? jacobians : nullptr);
+  }
+}
+
+BENCHMARK(BM_BAAutoDiff)->ArgName("Residual")->Arg(0);
+BENCHMARK(BM_BAAutoDiff)->ArgName("Residual+Jacobian")->Arg(1);
+BENCHMARK(BM_BACodeGen)->ArgName("Residual")->Arg(0);
+BENCHMARK(BM_BACodeGen)->ArgName("Residual+Jacobian")->Arg(1);
+
+}  // namespace ceres
+
+BENCHMARK_MAIN();
diff --git a/internal/ceres/codegen/snavely_reprojection_error.h b/internal/ceres/codegen/snavely_reprojection_error.h
new file mode 100644
index 0000000..d3c9da0
--- /dev/null
+++ b/internal/ceres/codegen/snavely_reprojection_error.h
@@ -0,0 +1,92 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2020 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.
+//
+// Author: darius.rueckert@fau.de (Darius Rueckert)
+//
+//
+#ifndef CERES_INTERNAL_CODEGEN_SNAVELY_REPROJECTION_ERROR_H_
+#define CERES_INTERNAL_CODEGEN_SNAVELY_REPROJECTION_ERROR_H_
+
+#include "ceres/codegen/codegen_cost_function.h"
+#include "ceres/rotation.h"
+
+namespace test {
+
+struct SnavelyReprojectionErrorGen
+    : public ceres::CodegenCostFunction<2, 9, 3> {
+  SnavelyReprojectionErrorGen(double observed_x, double observed_y)
+      : observed_x(observed_x), observed_y(observed_y) {}
+
+  SnavelyReprojectionErrorGen() = default;
+  template <typename T>
+  bool operator()(const T* const camera,
+                  const T* const point,
+                  T* residuals) const {
+    T ox = CERES_LOCAL_VARIABLE(T, observed_x);
+    T oy = CERES_LOCAL_VARIABLE(T, observed_y);
+
+    // camera[0,1,2] are the angle-axis rotation.
+    T p[3];
+    ceres::AngleAxisRotatePoint(camera, point, p);
+
+    // camera[3,4,5] are the translation.
+    p[0] += camera[3];
+    p[1] += camera[4];
+    p[2] += camera[5];
+
+    // Compute the center of distortion. The sign change comes from
+    // the camera model that Noah Snavely's Bundler assumes, whereby
+    // the camera coordinate system has a negative z axis.
+    const T xp = -p[0] / p[2];
+    const T yp = -p[1] / p[2];
+
+    // Apply second and fourth order radial distortion.
+    const T& l1 = camera[7];
+    const T& l2 = camera[8];
+    const T r2 = xp * xp + yp * yp;
+    const T distortion = T(1.0) + r2 * (l1 + l2 * r2);
+
+    // Compute final projected point position.
+    const T& focal = camera[6];
+    const T predicted_x = focal * distortion * xp;
+    const T predicted_y = focal * distortion * yp;
+
+    // The error is the difference between the predicted and observed position.
+    residuals[0] = predicted_x - ox;
+    residuals[1] = predicted_y - oy;
+
+    return true;
+  }
+
+#include "tests/snavelyreprojectionerrorgen.h"
+  double observed_x;
+  double observed_y;
+};
+
+}  // namespace test
+#endif  // CERES_INTERNAL_CODEGEN_SNAVELY_REPROJECTION_ERROR_H_