RootBA对于输出结果的统计非常细致,我们可以关注一下作者所关注的求解器benchmark信息有哪些。以及,他是怎么输出的?
我们主要关注bal.cpp中的main函数。
template <class Scalar>
BalProblem<Scalar> load_normalized_bal_problem(
const BalDatasetOptions& options, DatasetSummary* dataset_summary,
PipelineTimingSummary* timing_summary)
在问题载入的接口中,我们就可以看到,有两个summary的信息,这就是用于数据集和时间信息的统计。其中,DatasetSummary的信息吧阔对于相机个数,观测个数等参数的记录。而TimingSummary包括加载时间等参数。
struct PipelineTimingSummary {
double load_time = 0; // load from file
double preprocess_time = 0; // normalize, perturb, convert
double optimize_time = 0; // run bundle adjustment
double postprocess_time = 0; // save result
};
看到这些,第一感觉是参数记录并不细致,那更进一步的信息在哪里存储呢?
回到bal.cpp,你会发现,其实刚才提到的两个struct,只不过是BalPipelineSummary这个模块的一部分。除此之外,它还包含了另外一个结构体,那就是SolverSummary。而它就是一个庞然大物了。在solver_summary.hpp里面,可以看到它所统计的信息包括:
针对每一次迭代的统计信息,有IterationSummary这个结构体进行保存,其中包括:梯度,jacobian求解耗时,qr求解耗时,dump时间,代码对应的stage1,stage2的时间。每次迭代的IterationSummary被作为一个结构体进行保存。除了迭代的信息,在solver_summary中,还包括了对于最后结果的统计,求解的初始状态,求解的终止状态。
这样完成了信息记录后,在最后,log需要被存储,那么是怎么存储的呢》
在bal.cpp的最后几行。我们可以看到其信息被作为一个log数据结构单独存储。最后的存储竟然还用到了元编程。
BaLog log;
log_summary(log, summary);
log.save_json(options.solver.log);
}
总结:在求解最后的数据统计上,作者的使用方法真的使用的非常细心。在之前看的求解代码中,我们对于中间数据的统计往往采用glog的方式进行输出,而没有在意对于它们的结构化存储和统计,而rootBA作者贼通过在求解过程中引入log接口的形式,详细的记录了求解的数据细节。最后元编程的操作现在还不是很理解。