3

我正在分析我的代码并优化我可以做的一切,归结为一个看起来像这样的函数:

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()

  1. 我替换abs()fabs()它,它提高了大约 10% 的速度,这对我来说没有多大意义,因为我多次听说它们对于浮点数是相同的,abs()应该始终使用。这是不真实的还是我错过了什么?

  2. 评估可以进一步提高性能的双精度绝对值的最快方法是什么?

如果这很重要,我会g++在 linux X86_64 上使用。

4

4 回答 4

6

进行所有 3 次计算。将结果粘贴到 3 元素数组中。使用非分支算法找到正确的数组索引。返回那个结果。

IE,

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保存三个计算的结果。使用&代替&&很重要。

这消除了您的分支预测问题。最后,确保循环代码可以看到实现,这样就可以消除调用开销。

于 2013-05-23T02:35:28.287 回答
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
于 2013-05-23T04:03:16.063 回答
1

您是否尝试过像这样展开 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;
}
于 2013-05-23T02:14:15.620 回答
0

我会查看通过调用 fabs() 生成的程序集。这可能是函数调用的开销。如果是这样,请将其替换为内联解决方案。如果检查绝对值的内容确实很昂贵,请尝试按位和 (&),位掩码除符号位之外的任何地方都为 1。不过,我怀疑这会比标准库供应商的 fabs() 生成的便宜。

于 2013-05-23T02:28:50.063 回答