2010-11-12 194 views
1

我们有一个测量数据处理应用程序,目前所有数据保存为C++ float,这意味着我们的x86/Windows平台上的32位/ 4位。 (32位Windows应用程序)。x86 4字节浮点数与8字节双精度浮点数(对比long long)?

由于精度成为问题,所以有讨论转移到另一种数据类型。目前讨论的选项切换到double(8字节)或在__int64(8字节)之上实现固定的十进制类型。

使用__int64为基本类型的固定小数的解决方案,即使是讨论的原因是,有人声称double性能(仍然)比处理float小号显著恶化,我们可能会看到显著的性能优势使用原生整数类型存储我们的号码。 (请注意,我们真的会很好,固定小数精度,虽然代码显然会变得更加复杂。)

显然我们需要在最后进行基准测试,但是我想问一下,更糟糕的是,看着现代处理器有什么真相?我猜大数组可能会混淆缓存命中更多的浮动,但否则我真的不明白他们如何可以在性能上有所不同?

+1

“[双打]混乱的缓存命中更多的漂浮,但否则我真的不明白他们如何可以在性能上有所不同?”呃......是不是计算出更准确的结果,至少是一个可能更费时的任务呢?我从来没有对这些东西进行过基准测试......我会更加怀疑像日志或权力这样更复杂的功能。 – 2010-11-12 09:14:10

+3

为什么不写一个简单的压力测试来比较算术运算与浮点数组相比阵列加倍和验证结果的第一手? – Saul 2010-11-12 09:16:02

回答

5

这取决于你做什么。在当前的x86和POWER架构处理器上,double上的加法,减法和乘法与在float上的加法,减法和乘法一样快。由于它们的运行时间取决于期望的精确度,所以除法,平方根和超越函数(exp,log,sin,cos等)通常会显着地慢于双重参数。

如果你走的是定点,乘法和除法需要用长整数乘/除指令来实现,这些指令通常比在doubles上的算术要慢(因为处理器没有为其优化太多)。更何况,如果你在32位模式下运行,需要从几个32位长的乘法合成一个长64位乘以128位结果!

缓存利用率在这里是一个红色的鲱鱼。 64位整数和双精度是相同的大小 - 如果你需要32位以上的数据,无论如何你都会接受惩罚。

+0

感谢您指出我们无论如何都需要64位 - 至少缓存参数现在不在窗口中! – 2010-11-12 12:14:09

0

使用各种SIMD指令集,您可以以与单一成本相同的成本执行4个单精度浮点运算,本质上,您可以将4个浮点数打包到一个128位寄存器中。当切换到双打时,您只能将2个双打装入这些寄存器,因此您只能同时进行两个操作。

+0

只要您的代码不是自动矢量化的(我从来没有在任何不完全无关的循环中看到这样的工作),或者使用内在函数,它使用标量SSE或x87浮点运算。它们都只将一个值打包到一个寄存器中,所以较低的SIMD宽度并不重要。 – 2010-11-12 09:30:28

2

实现64个固定点并不是很有趣。特别是对于更复杂的功能,如Sqrt或对数。对于简单操作(如添加),整数可能会更快一些。你需要处理整数溢出。在实施四舍五入时你需要小心,否则错误很容易积累。

我们在C#项目中实现了固定点,因为我们需要确定性,即.net上的浮点不能保证。这是相当痛苦的。一些公式包含x^3 bang int溢出。除非你有真正令人信服的理由不使用浮点数或双精度数来代替定点数。

来自SSE2的SIMD指令进一步使比较复杂化,因为它们允许同时对多个浮点数(4个浮点数或2个双精度数)进行操作。我会使用两倍,并尝试利用这些说明。所以double可能会比float更慢,但与int整合比较困难,我更喜欢float/double over fixedpoint是大多数场景。

1

总是最好测量而不是猜测。是的,在许多体系结构中,double的计算处理数据是两次数据,因为计算结果为float(并且long doubles仍然较慢)。然而,正如其他答案和对这个答案的评论所指出的那样,x86架构并不遵循与ARM处理器,SPARC处理器等相同的规则。在x86 float s,double s和long double s都是转换为long double s进行计算。我应该知道这一点,因为转换导致x86结果比SPARC更准确,并且Sun经历了很多麻烦以获得对Java不太准确的结果,sparking some debate(请注意,该页面是从1998年开始的,此后发生了变化) 。

此外,doubles上的计算内置于CPU中,其中将使用软件写入固定十进制数据类型的计算并可能较慢。

你应该能够找到一个体面的固定大小的小数库并进行比较。

+2

至少在x86上,基本浮点运算(加/减/乘)总是以大于80位('长双精度)的精度执行,然后四舍五入到当前指定的精度。大多数基本指令在'float','double'和'long double'上都有相同的时间(注意大多数Windows编译器都有'double double' =='double' - 他们永远不会使用80位浮点数)。 – 2010-11-12 09:27:08

+2

@Fabian Giesen,实际上,如果启用SSE2,浮动计算不再使用x87硬件完成。 – avakar 2010-11-12 09:47:49

+0

它们不再使用x87 *指令*,但是它是相同的硬件,它执行SSE2和x87代码,并且加法器/乘法器仍然支持全宽操作,无需额外的成本,即使结果不是'写回寄存器。 – 2010-11-12 10:08:37

3

我很难理解的理由是,“比float慢两倍,我们将使用64位int”。猜测性能一直是需要大量经验的黑色艺术,在今天的硬件上,考虑到要考虑的因素数量,情况更糟糕。即使测量也很困难。我知道几种情况,其中微型基准借给了一种解决方案,但在上下文测量中表明另一种更好。

首先注意到,已经给出的两个因素解释了比float更慢的双重性能,这两个因素并不相关:所需带宽与双64位int相同,而SSE2矢量化将带来优势双...

然后考虑比使用整数计算将增加整数寄存器和计算单位的压力,当浮动点显然将保持不变。 (我已经看到了以double为单位进行整数计算的情况,因为增加了计算单位而获胜)

所以我怀疑滚动你自己的定点算法会比使用double更有优势(但我可以显示错误的措施)。

4

查找它。两家公司和英特尔公司都在其网站上免费提供的PDF文档中公布CPU的指令延迟。

然而,大部分,性能不会有显著不同,或有两个原因:

使用的x87 FPU,而不是SSE时
  • ,所有浮点操作都在80计算位内部精度,然后四舍五入,这意味着实际计算对于所有浮点类型同样昂贵。然后唯一的代价是与内存相关的(就CPU高速缓存和内存带宽使用而言,这只是floatdouble之间的一个问题,但如果与int64比较则无关紧要)
  • 有或没​​有SSE,几乎所有浮点操作是流水线操作。当使用SSE时,double指令可能(我没有看过这个)比它们的float等价物具有更高的等待时间,但吞吐量是相同的,因此应该可以用doubles实现类似的性能。

这也不是一个固定点数据类型实际上会更快。它可能,但在某些操作之后保持此数据类型一致的开销可能会超过节省。现代CPU上的浮点操作相当便宜。他们有一点延迟,但如前所述,他们通常是流水线,可能隐藏这个成本。

所以我的建议是:

  1. 写一些快速测试。编写执行大量浮点运算的程序应该不会那么困难,然后测量double版本相对于float版本的运行速度要慢多少。
  2. 看它的说明书,看看自己是否有floatdouble计算
0

正如许多人所说之间的任何显著的性能差异,64位INT可能是不值得的,如果双是一种选择。至少在SSE可用时。这可能会在各种微控制器上有所不同,但我想这不是你的应用程序。如果你需要更多精确的浮点数,你应该记住this operation is sometimes problematic浮点数和双精度,并且对整数更精确。