浮点数去规范化问题对性能的影响

在计算机科学中,浮点数的去规范化(下溢)问题是一个长期存在的问题,它对计算性能有着显著的影响。十年前,当Pentium 4 CPU首次出现这个问题时,就不得不面对它。然而,令惊讶的是,即使在现代硬件上,这种性能影响也没有得到缓解。StackOverflow上的一位用户报告称,在“flush to zero”模式下,Core i7的处理速度比处理去规范化数时快47倍。这是一个严重的问题。

出于好奇,在多种架构上运行了StackOverflow上的代码。以下是结果——首先,想解释一下为什么对这些结果不满意。

什么是去规范化数?

如果对IEEE浮点数感到困惑,需要阅读IEEE 754标准之前的浮点数历史,会发现它们更加神奇。不能真正写出可移植的代码:在某些架构上,将一个数乘以1.0会产生一个(略有不同)的数,在其他架构上,减去两个不同的数可能得到0,还有另一个架构要求在比较0之前将一个数乘以1.0... 可以在这里找到更多的例子(PDF)。

浮点数的一个特别问题是它们在0和第一个非零数之间有一个很大的“间隙”——要理解这个间隙,需要回到浮点数是如何构造的。

假设有3位十进制数字用于有效数字(SSS),一位十进制数字用于指数(E),两者都是有符号的。数是这样构造的:1.SSS * 10^E,其中1总是附加到(非零)数前面,以便在整个范围内保持十进制位数恒定(这对二进制来说更好,因为1是最大数字)。无论如何,用这种表示法,可以表示从1.999 * 10^9(最远离零)到1.000 * 10^(-9)(最接近零)的数。

现在,请注意,最小非零数和下一个数(即1.001*10^(-9))之间的距离等于0.001 * 10^(-9) == 10^(-12),这比1.000*10^(-9)和零之间的距离要小得多。

例如,在水平轴上有: 0 ---------- ----------- 1.000*10^(-9) -- 1.001*10^(-9) -- 1.002*10^(-9) -- ....

这个间隙的问题在于:如果从1.001*10^(-9)中减去1.000*10^(-9),不能在上述表示法(1.SSS*10^E)中表示这个差值——需要一个两位数的指数(-12)来表示。必须要么“将其冲零”,要么扩展表示法。

解决方案

“冲零”意味着当减去两个不相等的数时,可能会得到零,比如1.005*10^(-9)和1.000*10^(-9)。这可能是一个问题,例如,如果使用浮点数来测量时间,程序运行得太快:帧之间的时间差在“冲零”模式下会变成零,这可能导致除以零或意外的停止运动。

去规范化数是一个特例:为它们保留一个特定的指数值,并在前面不是附加1,而是0:即它们的形式是0.SSS * 10^(-9)(注意,指数固定在最低可能的值)。这意味着这些数在接近零时会失去小数位(即精度),所以实际上午餐不是免费的,仍然必须为此付出代价(即回到前面的例子,需要考虑到一旦程序运行得更快,时间差会变得不那么精确)。

然而,午餐甚至更贵——去规范化数会带来性能损失。

在至少几年前的几种处理器上运行了StackOverflow上的代码:3.0 Ghz Xeon X5450(2007年),2.2 Ghz Athlon 64 X2(2006年?),Atom N260(2009年),Cell(2006年),PowerPC 970FX(2003年),Intel Pentium 4(2002年),Cortex A8(来自Beagleboard xM)- 2009年。

必须强调没有改变代码——也就是说,它仍然是标量的。这对上述大多数CPU来说是次优的,特别是Cortex-A8,其中VFP比Electrolux吸尘器更好,所以这个“基准测试”并不代表优化后的代码。尽管如此,不认为标量代码的性能不应该受到影响。

结果如下:

Pentium 4受到的打击最大——这是第一次遇到这个问题的架构。它在去规范化数上大约慢了103倍,但这并不新鲜。然而,令人不悦的是,多年后Intel仍然在努力使去规范化数的性能更好——Xeon是受影响第二大的处理器,在去规范化数上几乎慢了40倍,根据StackOverflow上的帖子,Core i7在这方面甚至更糟。

x86/x64 CPU在性能上名列前茅,而RISC似乎受到的影响很小。请注意,SPU始终以“冲零”模式运行,完全不受影响——或者总是受到影响,这取决于是需要性能还是数值稳定性。它被包括在这里是为了完整性,也是为了提供一些尺度。

看起来RISC架构的胜利,对吧?嗯,如果考虑绝对时间的话:

CPU Norm Denorm Xeon 5450 0.57 sec 22.6 sec Athlon64 0.83 sec 27.8 sec PowerPC 970FX (G5) 1.5 sec 3.5 sec Pentium 4 2.25 sec 230 sec (!!) Cell SPU 3.28 sec (not applicable) Cell PPU 3.7 sec 11.67 sec Atom N260 7.35 sec 120 sec (!) Cortex A8 (VFP!) 25.5 sec 44 sec
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485