避免在C++中使用非正规值

Nat*_*han 24 c++ floating-point performance

在长时间搜索性能错误后,我读到了非正规浮点值.

显然,非规范化的浮点值可能是主要的性能问题,如此问题所示: 为什么将0.1f更改为0会使性能降低10倍?

我有一个Intel Core 2 Duo,我正在使用gcc进行编译-O2.

那我该怎么办?我可以以某种方式指示g ++避免非正规值吗?如果没有,我可以以某种方式测试一个float是否是非正规的?

Ste*_*non 23

等待.在您执行任何操作之前,您是否确实知道您的代码遇到了非正常值,并且它们会对性能产生可测量的影响?

假设您知道,如果关闭非正规支持,您是否知道您使用的算法是否稳定?得到错误答案快10倍通常不是一个好的性能优化.

抛开这些问题:

  • 如果要检测非正常值以确认它们的存在,则有几个选项.如果您有C99标准库或Boost,则可以使用该fpclassify宏.或者,您可以将数据的绝对值与最小正正常数进行比较.

  • 您可以将硬件设置为将非正常值刷新为零(FTZ),或将非正常输入视为零(DAZ).最简单的方法是,如果在您的平台上得到适当支持,可能是使用fesetenv( )C头中的函数fenv.h.但是,这是C标准中支持最少的功能之一,无论如何都具有固有的平台特性.您可能只想使用一些内联汇编来直接将FPU状态设置为(DAZ/FTZ).


Pot*_*ter 14

您可以使用测试浮点数是否非正规

#include <cmath>

if ( std::fpclassify( flt ) == FP_SUBNORMAL )
Run Code Online (Sandbox Code Playgroud)

(警告:我不确定这会在实践中全速执行.)

在C++ 03中,这段代码在实践中对我有用,

#include <cmath>
#include <limits>

if ( flt != 0 && std::fabsf( flt ) < std::numeric_limits<float>::min() ) {
    // it's denormalized
}
Run Code Online (Sandbox Code Playgroud)

要确定应用此位置,可以使用基于样本的分析器(如Shark,VTune或Zoom)来突出显示非正常值减慢的指令.微观优化,甚至超过其他优化,在没有前后分析的情况下完全没有希望.

  • 在大多数情况下使用`std :: isnormal`并不能达到你想要的效果,因为`isnormal`在其参数为零或无穷大或NaN时返回'0'(当寻找非正常的档位时,你通常*只*正在寻找非正规而不是那些在现代硬件上快速处理的其他值. (3认同)
  • 你是什​​么意思,没有性能损失?你确定吗?我写了一个小测试程序,显示添加值为 exp(-100) 的浮点比值为 0.1 时慢 10 倍。我在这里完全错了吗? (2认同)
  • @StephenCanon哎呀,零是真正的交易破坏者.谢谢. (2认同)
  • 这个答案将检测非正规,但不是一个避免性能损失的好方法.更好的是设置浮点单元的控制标志以刷新其他人提到的非正规. (2认同)

Han*_*ant 7

大多数数学协处理器都可以选择将非正规值截断为零.在x86上,它是MXCSR控制寄存器中的FZ(Flush to Zero)标志.检查您的CRT实现以获得支持功能以设置控制寄存器.它应该在<float.h>,类似于_controlfp()的东西.选项位通常在#defined符号中有"FLUSH".

设置此项后,请仔细检查您的数学结果.无论如何,这是你应该做的事情,得到非正常人是健康问题的标志.


das*_*esy 5

在 gcc 中拥有(清零)FTZ(假设下溢被默认屏蔽):

#define CSR_FLUSH_TO_ZERO         (1 << 15)
unsigned csr = __builtin_ia32_stmxcsr();
csr |= CSR_FLUSH_TO_ZERO;
__builtin_ia32_ldmxcsr(csr);
Run Code Online (Sandbox Code Playgroud)

以防从名称中看不出来,__builtin_ia32_stmxcsr并且__builtin_ia32_ldmxcsr仅当您针对 x86 处理器时才可用。ARM、Sparc、MIPS 等都需要使用这种方法单独的特定于平台的代码。