我应该对包含无效值的数据集使用浮点数NaN或浮点数+布尔值吗?

And*_*ndt 23 c++ floating-point performance nan

我需要处理大量数据,并对每个数据集进行数学密集型操作.其中大部分类似于图像处理.但是,由于直接从物理设备读取此数据,因此许多像素值可能无效.

这使得NaN的属性表示非数字的值,并且在算术运算上的扩展非常引人注目.然而,它似乎也需要关闭一些优化,如gcc的-ffast-math,而且我们需要跨平台.我们当前的设计使用一个简单的结构,它包含一个浮点值和一个指示有效性的bool.

虽然看起来NaN的设计考虑到了这种用途,但其他人认为它比它的价值更麻烦.有没有人根据他们对IEEE754的更亲密的体验而考虑到性能?

Kra*_*lew 19

简介:为了最严格的可移植性,请不要使用NaN.使用单独的有效位.例如像Valid这样的模板.但是,如果您知道您将只运行IEEE 754-2008计算机,而不是IEEE 754-1985(见下文),那么您可能会使用它.

为了提高性能,在大多数可以访问的计算机上使用NaN可能会更快.但是,我已经在几台提高NaN处理性能的机器上参与了FP的硬件设计,因此有一种趋势可以使NaN更快,特别是,信号NaN应该很快比Valid更快.

详情:

并非所有浮点格式都有NaN.并非所有系统都使用IEEE浮点.IBM hex浮点仍然可以在某些机器上找到 - 实际上是系统,因为IBM现在在更新的机器上支持IEEE FP.

此外,IEEE浮点本身在IEEE 754-1985中与NaNs存在兼容性问题.例如,请参阅维基百科http://en.wikipedia.org/wiki/NaN:

1985年的原始IEEE 754标准(IEEE 754-1985)仅描述了二进制浮点格式,并未指定如何标记信号/静默状态.在实践中,有效数字的最重要部分确定NaN是信号还是静音.产生了两种不同的实现,具有相反的含义.*大多数处理器(包括Intel/AMD x86-32/x86-64系列,Motorola 68000系列,AIM PowerPC系列,ARM系列和Sun SPARC系列)将信号/静音位设置为非零如果NaN是安静的,如果NaN是信号则为零.因此,在这些处理器上,该位表示'is_quiet'标志.*在PA-RISC和MIPS处理器生成的NaN中,如果NaN是安静的,则发信号/静默位为零,如果NaN发信号,则为非零.因此,在这些处理器上,该位表示'is_signaling'标志.

如果您的代码可能在较旧的HP机器或当前的MIPS机器(在嵌入式系统中无处不在)上运行,那么您不应该依赖于NaN的固定编码,而应该为您的特殊NaN设置依赖于机器的#ifdef.

IEEE 754-2008标准化了NaN编码,因此这种情况越来越好.这取决于你的市场.

至于性能:许多机器在执行涉及SNaN(必须陷阱)和QNaN(不需要陷阱,即哪些可能很快 - 以及哪些正在获取)的计算时,基本上陷阱或以其他方式对性能产生重大打击在我们说话的时候,某些机器会更快.)

我可以自信地说,在较旧的机器上,特别是较旧的Intel机器上,如果你关心性能,你不想使用NaN.例如,http://www.cygnus-software.com/papers/x86andinfinity.html说"英特尔奔腾4非常糟糕地处理无穷大,NAN和非正规....如果您编写的代码可以按照以下速率添加浮点数每个时钟周期一个,然后输入无穷大作为输入,性能下降.很多.很大.... NANs甚至更慢.加上NANs需要大约930个周期... ... Denormals有点棘手测量."

得到图片?使用NaN比使用正常的浮点运算慢近1000倍?在这种情况下,几乎可以保证使用像Valid这样的模板会更快.

但是,请参阅"奔腾4"?这是一个非常古老的网页.多年来,像我这样的人一直在说"QNaNs应该更快",并且它已经慢慢占据了.

最近(2009年),微软表示http://connect.microsoft.com/VisualStudio/feedback/details/498934/big-performance-penalty-for-checking-for-nans-or-infinity "如果你做数学数组包含大量NaN或无穷大的双重数据,会有一个数量级的性能损失."

如果我感到被迫,我可能会在某些机器上运行微基准测试.但你应该了解情况.

这应该是改变的,因为快速使QNaN变得并不困难.但它一直是鸡和蛋的问题:像我工作的那些硬件人说"没有人使用NaN,所以我们赢了;不能让它们快",而软件人员不使用NaN,因为它们很慢.然而,潮流正在缓慢变化.

哎呀,如果你正在使用gcc并希望获得最佳性能,你可以启用"-ffinite-math-only ...允许对浮点运算进行优化的优化,假设参数和结果不是NaN或+ -Infs".大多数编译器也是如此.

顺便说一句,你可以像我一样google,"NaN性能浮动点"并检查自己.和/或运行自己的微基准测试.

最后,我一直在假设您使用的是模板

template<typename T> class Valid {
    ...
    bool valid;
    T value;
    ...
};
Run Code Online (Sandbox Code Playgroud)

我喜欢这样的模板,因为它们不仅可以为FP带来"有效性跟踪",还可以为整数(有效)等带来"有效性跟踪".

但是,他们可能会付出很大的代价.这些操作可能并不比旧机器上的NaN处理昂贵得多,但数据密度可能非常差.sizeof(有效)有时可能是2*sizeof(float).这种不良密度可能会比所涉及的操作损害性能.

顺便说一句,您应该考虑模板特化,以便有效使用NaN(如果它们是可用且快速的),否则使用有效位.

template <> class Valid<float> { 
    float value; 
    bool is_valid() { 
        return value != my_special_NaN; 
    } 
}
Run Code Online (Sandbox Code Playgroud)

等等

无论如何,你最好尽可能少的有效位,并将它们打包到别处,而不是有效接近值.例如

struct Point { float x, y, z; };
Valid<Point> pt;
Run Code Online (Sandbox Code Playgroud)

比(密度明智)更好

struct Point_with_Valid_Coords { Valid<float> x, y, z; };
Run Code Online (Sandbox Code Playgroud)

除非您使用NaN - 或其他一些特殊编码.

struct Point_with_Valid_Coords { float x, y, z; bool valid_x, valid_y, valid_z };
Run Code Online (Sandbox Code Playgroud)

介于两者之间 - 但是你必须自己完成所有代码.

顺便说一下,我一直在假设您使用的是C++.如果FORTRAN或Java ......

底线:单独的有效位可能更快,更便携.

但NaN的处理正在加速,有一天很快就会好起来的

顺便说一句,我的偏好:创建一个有效的模板.然后,您可以将其用于所有数据类型.如果它有帮助,专门为NaNs.虽然我的生活使事情变得更快,但恕我直言,通常更重要的是使代码清洁.


归档时间:

查看次数:

2173 次

最近记录:

13 年,7 月 前