Expand reporting of timing information.
1. Add an ExecutionSummary object to record execution
information about Ceres objects.
2. Add an EventLogger object to log events in a function call.
3. Add a ScopedExecutionTimer object to log times in ExecutionSummary.
4. Instrument ProgramEvaluator and all the linear solvers
to report their timing statistics.
5. Connect the timing statistics to Summary::FullReport.
6. Add high precision timer on unix systems using
gettimeofday() call.
7. Various minor clean ups all around.
Change-Id: I5e09804b730b09535484124be7dbc1c58eccd1d4
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index 50c96e5..3dfbb67 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -177,6 +177,8 @@
FILE* fptr_;
};
+// Iterate over each of the groups in order of their priority and fill
+// summary with their sizes.
void SummarizeOrdering(ParameterBlockOrdering* ordering,
vector<int>* summary) {
CHECK_NOTNULL(summary)->clear();
@@ -308,6 +310,7 @@
void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
ProblemImpl* original_problem_impl,
Solver::Summary* summary) {
+ EventLogger event_logger("TrustRegionSolve");
double solver_start_time = WallTimeInSeconds();
Program* original_program = original_problem_impl->mutable_program();
@@ -369,6 +372,7 @@
" currently broken. Ignoring Solver::Options::lsqp_iterations_to_dump";
}
+ event_logger.AddEvent("Init");
// Evaluate the initial cost, residual vector and the jacobian
// matrix if requested by the user. The initial cost needs to be
// computed on the original unpreprocessed problem, as it is used to
@@ -392,6 +396,8 @@
return;
}
+ event_logger.AddEvent("InitialEvaluate");
+
original_program->SetParameterBlockStatePtrsToUserStatePtrs();
// If the user requests gradient checking, construct a new
@@ -430,12 +436,15 @@
}
}
+ event_logger.AddEvent("ConstructOrdering");
+
// Create the three objects needed to minimize: the transformed program, the
// evaluator, and the linear solver.
scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
problem_impl,
&summary->fixed_cost,
&summary->error));
+ event_logger.AddEvent("CreateReducedProgram");
if (reduced_program == NULL) {
return;
}
@@ -484,6 +493,7 @@
// Ensure the program state is set to the user parameters on the way out.
original_program->SetParameterBlockStatePtrsToUserStatePtrs();
+ event_logger.AddEvent("FinalEvaluate");
summary->postprocessor_time_in_seconds =
WallTimeInSeconds() - post_process_start_time;
return;
@@ -491,6 +501,7 @@
scoped_ptr<LinearSolver>
linear_solver(CreateLinearSolver(&options, &summary->error));
+ event_logger.AddEvent("CreateLinearSolver");
if (linear_solver == NULL) {
return;
}
@@ -527,6 +538,9 @@
problem_impl->parameter_map(),
reduced_program.get(),
&summary->error));
+
+ event_logger.AddEvent("CreateEvaluator");
+
if (evaluator == NULL) {
return;
}
@@ -549,6 +563,8 @@
}
}
+ event_logger.AddEvent("CreateIIM");
+
// The optimizer works on contiguous parameter vectors; allocate some.
Vector parameters(reduced_program->NumParameters());
@@ -561,6 +577,7 @@
summary->preprocessor_time_in_seconds =
minimizer_start_time - solver_start_time;
+ event_logger.AddEvent("FinalInit");
// Run the optimization.
TrustRegionMinimize(options,
reduced_program.get(),
@@ -569,6 +586,7 @@
linear_solver.get(),
parameters.data(),
summary);
+ event_logger.AddEvent("Minimize");
// If the user aborted mid-optimization or the optimization
// terminated because of a numerical failure, then return without
@@ -612,15 +630,31 @@
summary->termination_type = NUMERICAL_FAILURE;
summary->error = "Unable to evaluate the final cost.";
LOG(ERROR) << summary->error;
+
+ event_logger.AddEvent("PostProcess");
return;
}
// Ensure the program state is set to the user parameters on the way out.
original_program->SetParameterBlockStatePtrsToUserStatePtrs();
+ const map<string, double>& linear_solver_time_statistics =
+ linear_solver->TimeStatistics();
+ summary->linear_solver_time_in_seconds =
+ FindWithDefault(linear_solver_time_statistics, "LinearSolver::Solve", 0.0);
+
+ const map<string, double>& evaluator_time_statistics =
+ evaluator->TimeStatistics();
+
+ summary->residual_evaluation_time_in_seconds =
+ FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
+ summary->jacobian_evaluation_time_in_seconds =
+ FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
+
// Stick a fork in it, we're done.
summary->postprocessor_time_in_seconds =
WallTimeInSeconds() - post_process_start_time;
+ event_logger.AddEvent("PostProcess");
}
void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
@@ -764,7 +798,7 @@
// any further.
summary->termination_type = FUNCTION_TOLERANCE;
- double post_process_start_time = WallTimeInSeconds();
+ const double post_process_start_time = WallTimeInSeconds();
// Evaluate the final cost, residual vector and the jacobian
// matrix if requested by the user.
if (!Evaluator::Evaluate(original_program,
@@ -809,7 +843,7 @@
Vector original_parameters = parameters;
- double minimizer_start_time = WallTimeInSeconds();
+ const double minimizer_start_time = WallTimeInSeconds();
summary->preprocessor_time_in_seconds =
minimizer_start_time - solver_start_time;
@@ -828,7 +862,7 @@
return;
}
- double post_process_start_time = WallTimeInSeconds();
+ const double post_process_start_time = WallTimeInSeconds();
// Push the contiguous optimized parameters back to the user's parameters.
reduced_program->StateVectorToParameterBlocks(parameters.data());
@@ -1026,9 +1060,13 @@
ProblemImpl* problem_impl,
double* fixed_cost,
string* error) {
+ EventLogger event_logger("CreateReducedProgram");
+
CHECK_NOTNULL(options->linear_solver_ordering);
Program* original_program = problem_impl->mutable_program();
scoped_ptr<Program> transformed_program(new Program(*original_program));
+ event_logger.AddEvent("TransformedProgram");
+
ParameterBlockOrdering* linear_solver_ordering =
options->linear_solver_ordering;
@@ -1043,6 +1081,8 @@
return NULL;
}
+ event_logger.AddEvent("RemovedFixedBlocks");
+
if (transformed_program->NumParameterBlocks() == 0) {
if (transformed_program->NumResidualBlocks() > 0) {
*error = "Zero parameter blocks but non-zero residual blocks"
@@ -1076,6 +1116,7 @@
(i < num_eliminate_blocks) ? 0 : 1);
}
}
+ event_logger.AddEvent("SchurOrdering");
if (!ApplyUserOrdering(problem_impl->parameter_map(),
linear_solver_ordering,
@@ -1083,6 +1124,7 @@
error)) {
return NULL;
}
+ event_logger.AddEvent("ApplyOrdering");
// If the user requested the use of a Schur type solver, and
// supplied a non-NULL linear_solver_ordering object with more than
@@ -1123,9 +1165,13 @@
LOG(WARNING) << msg;
}
+ event_logger.AddEvent("AlternateSolver");
+
// Since the transformed program is the "active" program, and it is mutated,
// update the parameter offsets and indices.
transformed_program->SetParameterOffsetsAndIndex();
+
+ event_logger.AddEvent("SetOffsets");
return transformed_program.release();
}