我正在分析我的代码并优化我所能做的一切,直到一个看起来像这样的函数:
double func(double a, double b, double c, double d, int i){
if(i > 10 && a > b || i < 11 && a < b)
return abs(a-b)/c;
else
return d/c;
}
在程序运行期间,它被调用了数百万次,并且分析器向我显示,大约80%的时间花在调用abs()
上。
我用abs()
替换fabs()
并且它提供了大约10%的加速速度,这对我来说没有多大意义,因为我多次听说它们对于浮点数和应始终使用abs()
。这是不真实还是我遗失了什么?
评估双倍绝对值的最快方法是什么?这可以进一步提高绩效?
如果重要,我在linux X86_64上使用g++
。
答案 0 :(得分:6)
完成所有3次计算。将结果粘贴在3元素数组中。使用非分支算法来查找正确的数组索引。返回结果。
即,
bool icheck = i > 10;
bool zero = icheck & (a > b);
bool one = !icheck & (b > a);
bool two = !zero & !one;
int idx = one | (two << 1);
return val[idx];
其中val
包含三次计算的结果。使用&
代替&&
非常重要。
这会消除您的分支预测问题。最后,确保循环代码可以看到实现,因此可以消除调用开销。
答案 1 :(得分:4)
有趣的问题。
double func(double a, double b, double c, double d, int i){
if(i > 10 && a > b || i < 11 && a < b)
return abs(a-b)/c;
else
return d/c;
}
首先想到的是:
我将假设a永远不等于b - 我的直觉是,你的数据集有50%的可能性,并且它允许一些有趣的优化。如果不是这样,那么我没有任何迹象表明Yakk还没有。
double amb = a - b;
bool altb = a < b; // or signbit(amb) if it proves faster for you
double abs_amb = (1 - (altb << 1)) * amb;
bool use_amb = i > 10 != altb;
return (use_amb * abs_amb + !use_amb * d) / c;
在构建工作时我注意到的目标之一是允许CPU执行管道中的某些并发;这可以这样说明:
amb altb i > 10
\ / \ /
abs_amb use_amb
\ / \
use_amb*abs_amb !use_amb*d
\ /
+ /c
答案 2 :(得分:1)
您是否尝试过展开if if:
double func(double a, double b, double c, double d, int i){
if(i > 10 && a > b)
return (a-b)/c;
if (i < 11 && a < b)
return (b-a)/c;
return d/c;
}
答案 3 :(得分:0)
我会看看通过调用fabs()生成的程序集。它可能是函数调用的开销。如果是这样,请使用内联解决方案替换它。如果它确实是检查昂贵的绝对值的内容,那么尝试按位和(&amp;)除了符号位之外的所有位置都是1。我怀疑这会比标准库供应商的fabs()生成的更便宜。