【发布时间】:2018-08-31 20:25:56
【问题描述】:
我正在研究加速大部分 C++ 代码的方法,这些代码具有用于计算雅可比的自动导数。这涉及在实际残差中做一些工作,但大部分工作(基于分析的执行时间)是计算雅可比。
这让我很惊讶,因为大多数雅可比是从 0 和 1 向前传播的,所以工作量应该是函数的 2-4 倍,而不是 10-12 倍。为了模拟大量的 jacobian 工作是什么样的,我做了一个超级最小的例子,只有一个点积(而不是真实情况下的 sin、cos、sqrt 等),编译器应该能够优化为单个返回值:
#include <Eigen/Core>
#include <Eigen/Geometry>
using Array12d = Eigen::Matrix<double,12,1>;
double testReturnFirstDot(const Array12d& b)
{
Array12d a;
a.array() = 0.;
a(0) = 1.;
return a.dot(b);
}
应该和
一样double testReturnFirst(const Array12d& b)
{
return b(0);
}
我很失望地发现,在没有启用快速数学的情况下,GCC 8.2、Clang 6 或 MSVC 19 都无法对充满 0 的矩阵的天真点积进行任何优化。即使使用快速数学 (https://godbolt.org/z/GvPXFy),GCC 和 Clang 中的优化也很差(仍然涉及乘法和加法),并且 MSVC 根本不做任何优化。
我没有编译器的背景,但这有什么原因吗?我相当肯定,在大部分科学计算中,能够进行更好的常数传播/折叠将使更多优化变得明显,即使常数折叠本身并没有导致加速。
虽然我对解释为什么不在编译器方面这样做很感兴趣,但我也对我可以在实际方面做些什么来使我自己的代码在面对这些类型的模式时更快。
【问题讨论】:
-
浮点数不是实数,它们有严格的正确性要求,明显的优化违反了这些要求。例如。
(1.0 / 3.0) * 3.0!=(1.0 * 3.0)/3.0因为舍入行为是完全指定的,所以你不能简单地取消 3. -
答案取决于
dot的实现。可能它不仅仅是一个带有累积的for循环,而是涉及重新缩放。难怪编译器不能优化它。 -
-ffast-math的意思是说“没有必要遵守标准”。 MSVC 等价于 fast-math 是/fp:fast如果你指定它,你可能会发现它做了一些优化。 -
添加
-ffast-math后,剩下的“问题”是显式矢量化,请参阅我的答案。 -
你可以在godbolt中看到选项。 -O3 用于 gcc/clang,/Ox 用于 MSVC。
标签: c++ compiler-construction eigen automatic-differentiation ceres-solver