Avoiding denormal values in C++
在搜索了很长一段时间的性能错误之后,我读到了关于非正规浮点值的内容。
显然,非规范化的浮点值可能是主要的性能问题,如本问题所示:为什么将0.1f更改为0会将性能降低10倍?
我有一个英特尔酷睿2二重奏,我正在用gcc编译,使用
那我该怎么办?我能指示g++避免非正规值吗?如果不是,我可以用某种方法测试一个
等待。在您做任何事情之前,您是否知道您的代码遇到了非正常值,并且它们对性能有可测量的影响?
假设您知道这一点,那么如果关闭非规范支持,您是否知道正在使用的算法是否稳定?更快地得到10倍的错误答案通常不是一个好的性能优化。
撇开这些问题不谈:
如果您想检测非正常值以确认它们的存在,您有几个选项。如果您有C99标准库或Boost,则可以使用
fpclassify 宏。或者,您可以将数据的绝对值与最小的正态数进行比较。您可以将硬件设置为将非规范值刷新为零(ftz),或者将非规范输入视为零(daz)。如果在您的平台上得到适当的支持,最简单的方法可能是在c header
fenv.h 中使用fesetenv( ) 函数。然而,这是C标准最不受广泛支持的特性之一,并且本质上是特定于平台的。您可能只需要使用一些内联程序集直接将fpu状态设置为(daz/ftz)。
您可以使用
1 2 3 | #include <cmath> if ( std::fpclassify( flt ) == FP_SUBNORMAL ) |
(警告:我不确定这是否会在实践中全速执行。)
在C++ 03中,这个代码在我的实践中起作用,
1 2 3 4 5 6 | #include <cmath> #include <limits> if ( flt != 0 && std::fabsf( flt ) < std::numeric_limits<float>::min() ) { // it's denormalized } |
号
要决定在何处应用,可以使用基于示例的分析器(如shark、vtune或zoom)来突出显示因非正常值而减慢的指令。微观优化,甚至比其他优化,在没有分析之前和之后是完全没有希望的。
大多数数学协处理器都有将非正规值截断为零的选项。在x86上,它是mxcsr控制寄存器中的fz(flush to zero)标志。检查CRT实现中是否有设置控制寄存器的支持函数。它应该在
设置此项后,再次检查数学结果。不管怎样,这是你应该做的,非正常化是健康问题的征兆。
在GCC中具有(flush to zero)ftz(假设下溢被默认屏蔽):
1 2 3 4 | #define CSR_FLUSH_TO_ZERO (1 << 15) unsigned csr = __builtin_ia32_stmxcsr(); csr |= CSR_FLUSH_TO_ZERO; __builtin_ia32_ldmxcsr(csr); |
如果从名称上看不明显,那么只有当您的目标是x86处理器时,
正如对其他答案的补充一样,如果您确实有非正规浮点值的问题,那么除了性能问题之外,您可能还有精度问题。
检查是否可以重新构造计算以使数字更大以避免损失精度和性能,这可能是一个好主意。
显然,您需要一些名为ftz(flush to zero)和daz(nonormals are zero)的CPU指令。
我在一个音频网站上找到了信息,但是他们与Intel文档的链接丢失了。它们显然是SSE2指令,因此它们应该在支持该指令的AMD CPU上工作。
我不知道你能在GCC中做些什么,以一种便携的方式强制执行它。但是,您始终可以编写内联程序集代码来使用它们。您可能需要强制GCC只使用SSE2进行浮点运算。