【发布时间】:2016-03-20 14:33:57
【问题描述】:
我正在编写一个函数来创建一个高斯滤波器(使用犰狳库),它可能是 2D 或 3D,具体取决于它接收到的输入的维数。代码如下:
template <class ty>
ty gaussianFilter(const ty& input, double sigma)
{
// Our filter will be initialized to the same size as our input.
ty filter = ty(input); // Copy constructor.
uword nRows = filter.n_rows;
uword nCols = filter.n_cols;
uword nSlic = filter.n_elem / (nRows*nCols); // If 2D, nSlic == 1.
// Offsets with respect to the middle.
double rowOffset = static_cast<double>(nRows/2);
double colOffset = static_cast<double>(nCols/2);
double sliceOffset = static_cast<double>(nSlic/2);
// Counters.
double x = 0 , y = 0, z = 0;
for (uword rowIndex = 0; rowIndex < nRows; rowIndex++) {
x = static_cast<double>(rowIndex) - rowOffset;
for (uword colIndex = 0; colIndex < nCols; colIndex++) {
y = static_cast<double>(colIndex) - colOffset;
for (uword sliIndex = 0; sliIndex < nSlic; sliIndex++) {
z = static_cast<double>(sliIndex) - sliceOffset;
// If-statement inside for-loop looks terribly inefficient
// but the compiler should take care of this.
if (nSlic == 1){ // If 2D, Gauss filter for 2D.
filter(rowIndex*nCols + colIndex) = ...
}
else
{ // Gauss filter for 3D.
filter((rowIndex*nCols + colIndex)*nSlic + sliIndex) = ...
}
}
}
}
正如我们看到的,在最里面的循环中有一个 if 语句,它检查第三维(nSlic)的大小是否等于 1。一旦在函数的开头计算,nSlic 就不会改变它值,所以编译器应该足够聪明来优化条件分支,我不应该失去任何性能。
但是...如果我从循环中删除 if 语句,我会获得性能提升。
if (nSlic == 1)
{ // Gauss filter for 2D.
for (uword rowIndex = 0; rowIndex < nRows; rowIndex++) {
x = static_cast<double>(rowIndex) - rowOffset;
for (uword colIndex = 0; colIndex < nCols; colIndex++) {
y = static_cast<double>(colIndex) - colOffset;
for (uword sliIndex = 0; sliIndex < nSlic; sliIndex++) {
z = static_cast<double>(sliIndex) - sliceOffset;
{filter(rowIndex*nCols + colIndex) = ...
}
}
}
}
else
{
for (uword rowIndex = 0; rowIndex < nRows; rowIndex++) {
x = static_cast<double>(rowIndex) - rowOffset;
for (uword colIndex = 0; colIndex < nCols; colIndex++) {
y = static_cast<double>(colIndex) - colOffset;
for (uword sliIndex = 0; sliIndex < nSlic; sliIndex++) {
z = static_cast<double>(sliIndex) - sliceOffset;
{filter((rowIndex*nCols + colIndex)*nSlic + sliIndex) = ...
}
}
}
}
使用g++ -O3 -c -o main.o main.cpp 编译并测量两种代码变体的执行时间后,我得到以下结果:
(1000 次重复,大小为 2048 的二维矩阵)
If-inside:
- 66.0453 秒
- 64.7701 秒
如果在外面:
- 64.0148 秒
- 63.6808 秒
如果 nSlic 的值甚至没有改变,为什么编译器不优化分支?我必须重组代码以避免for-loop 中的if-statement?
【问题讨论】:
-
我对你的问题感到困惑。您将 if 语句移出嵌套循环并惊讶于您的代码运行得更快?您是否希望编译器将您的第一个版本的代码转换为您的第二个版本?
-
我相信如果
if-statement 总是产生相同的结果,编译器会优化它。我的假设来自sorted vs. unsorted array。我想了解为什么不是这种情况,以及何时可以期待这样的编译器优化。 -
哦,我明白了。不过,这不是编译器的工作。处理器处理分支预测。
-
分支预测是一种物理内置于处理器本身的机制,以最大程度地减少循环对the pipeline中指令的影响,它与编译器优化无关。
-
@dpgomez:您想到的编译器优化称为
loop unswitching。如果你使用 gcc,你可能需要指定-O3或-funswitch-loops来启用它。
标签: c++ compiler-optimization branch-prediction