提问人:rjzii 提问时间:11/17/2023 最后编辑:rjzii 更新时间:11/17/2023 访问量:194
确定双精度值在功能上是否为零的最快方法是什么
What is the fastest way to determine if a double is functionally zero
问:
我正在努力重构一些处理大量浮点计算的代码以提高性能(根据 valgrind 的说法,我们大约 90% 的时间都花在这个函数上,所以优化很好);但是,如果我们遇到实际上为零的情况,那么我们可以分配零并继续前进。以前我们执行以下操作(假设内联函数):x
if (std::fabs(a - b) < std::numeric_limits<double>::epsilon()) {
// do things
}
在大多数情况下。而现在,当我知道它是零时,我正在改变以下模式:b = 0.0
b
if (std::abs(a) < std::numeric_limits<double>::epsilon()) {
// do things
}
这似乎为我们节省了几个周期。但是,是否可以采取更多措施来加快速度,或者这或多或少是门槛?
答:
6赞
ShadowRanger
11/17/2023
#1
我认为这与它将要得到的效率差不多。启用优化后,在 x86-64 系统上,生成的程序集为:if
- A(将 的值从全局常量存储器加载到寄存器;函数变成一个要加载的常量)
movsd
epsilon
- 一个(为了掩盖被检查中的负号位,这就是内联时所要做的)
andpd
double
std::abs
- A(比较两个寄存器)
comisd
- A(实际条件分支)
ja
如果为更现代的芯片编译,前三条指令将更改为前缀形式(我猜新版本在可用时效率更高),但除此之外不会更改。这几乎是便宜的。我想从理论上讲,如果你经常这样做,矢量化可能会让你用更少的指令对多个值做同样的工作,但是:v
- 编译器可能会自行解决这个问题,并且
- 如果没有,我会等待分析显示这是一个瓶颈,然后再进一步担心
据我所知,任何基于(您想要并被视为等效的地方)的解决方案,例如:std::fpclassify
FP_ZERO
FP_SUBNORMAL
switch (std::fpclassify(a)) {
case FP_SUBNORMAL:
case FP_ZERO:
// do things
break;
}
涉及相同的指令,加上三个(或)指令,以及三个条件跳转;几乎可以肯定,两个额外的比较和条件分支比加载单个常量更糟糕。andpd
comisd
ucomisd
1赞
bolov
11/17/2023
#2
您可以考虑的另一个优化是丢弃更多情况:
constexpr float near_zero_limit = 0.01f;
if (std::fabs(a - b) < near_zero_limit )
这并不是说测试更快,而是在更多情况下你不做剩下的工作。我不知道你的算法,也不知道这对你的计算是否有意义,但你可以搜索最大的算法,你可以接受将最终结果视为有效的零或从零开始察觉不到,如果这会带来更高的性能。near_zero_limit
评论
std::fpclassify()
不处理 epsilon,只处理 +/-0。double
switch
std::fpclassify
FP_ZERO
FP_SUBNORMAL