在 C++ 中加速双精度绝对值
Posted
技术标签:
【中文标题】在 C++ 中加速双精度绝对值【英文标题】:Speeding up double absolute value in C++ 【发布时间】:2013-05-23 02:07:57 【问题描述】:我正在分析我的代码并尽可能优化一切,最终得到一个看起来像这样的函数:
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()
上。
我用fabs()
替换了abs()
,它提高了大约10% 的速度,这对我来说没有多大意义,因为我多次听说它们对于浮点数是相同的,abs()
应该一直使用。这是不真实的还是我遗漏了什么?
评估可进一步提高性能的双精度绝对值的最快方法是什么?
如果这很重要,我在 linux X86_64 上使用 g++
。
【问题讨论】:
如果 IEEE,并且您想作恶并利用未定义的行为,您可以尝试double x = a - b; *(uint64_t *)&x &= ~(1ULL << 63); return x / c;
只是好奇 - a 是否永远等于 b?
@TonyD 假设不,这对我来说没关系。
【参考方案1】:
执行所有 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
保存三个计算的结果。使用&
而不是&&
很重要。
这消除了您的分支预测问题。最后,确保循环代码可以看到实现,这样就可以消除调用开销。
【讨论】:
我很好奇:分支预测问题相对经常出现,这种“非分支”选择技巧很经典,但即使在这种(诚然)简单的情况下,编译器似乎也没有对其进行优化. 公平地说,如果分支是可预测的,非优化版本可能会更快。除非我对其进行测试,否则我不相信 av=bove 代码会更快:我正在做 2-3 倍的工作! 是的,可能,但这正是您希望编译器介入并根据目标架构等进行优化的地方... @MatthieuM。这不仅仅是目标架构——编译器可能不知道输入是否以i > 10 && a > b || i < 11 && a < b
将它们分为四个(甚至两个)的顺序提供,这样分支预测几乎可以完美地工作,或者输入有有效地随机关系到测试。在这种情况下,运行时测量甚至代码自我修改可能会有所帮助。
感谢您的回答,这似乎是一种非常有趣的方法。我有一个疑问,你确定int idx = one & (two << 1);
是正确的吗?我可能遗漏了一些东西,但我相信这将始终为 0(我的猜测是,当两者都为真时,它会导致 01&10)。它可以与or
甚至xor
一起使用。【参考方案2】:
有趣的问题。
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
【讨论】:
+1 谢谢,这真的很有帮助!不过,我接受 Yakk 的回答,因为这个想法很相似,而且他是第一个。 很想知道这产生了多少改进。谢谢。 @c-urchin,我没有测试确切的情况,我的代码有点复杂,但是在使用这个想法之后,我的改进是 ~20%。【参考方案3】:您是否尝试过像这样展开 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;
【讨论】:
我试过这个。在 VS2010 中,它似乎比std::abs
或 fabs
慢 =/
因为在我的情况下 a > b 比 a
你已经在做比较了,所以在第一种情况下,它甚至不需要做 abs 或 fabs。【参考方案4】:
我会查看调用 fabs() 生成的程序集。这可能是函数调用的开销。如果是这样,请将其替换为内联解决方案。如果检查绝对值的内容确实很昂贵,请尝试按位和 (&),位掩码除符号位外的所有位置均为 1。不过,我怀疑这会比标准库供应商的 fabs() 生成的便宜。
【讨论】:
以上是关于在 C++ 中加速双精度绝对值的主要内容,如果未能解决你的问题,请参考以下文章