使用比浮动更快的双倍?

时间:2010-08-06 17:23:41

标签: c++ performance x86 intel osx-snow-leopard

双值存储更高的精度,是浮点数的两倍,但英特尔CPU是否针对浮点数进行了优化?

也就是说,对于+, - ,*和/?

,双重操作是否比浮点运算更快或更快

64位架构的答案是否有所改变?

8 个答案:

答案 0 :(得分:72)

没有一个“英特尔CPU”,特别是在哪些操作相对于其他人进行优化方面!但是大多数,在CPU级别(特别是在FPU内),都是这样的答案问题:

  

是双重操作同样快或   比+, - 的浮动操作更快,   *,和/?

是“是” - 在CPU中 ,除了somewhat slower for double than for float的除法和sqrt之外。 (假设您的编译器使用SSE2进行标量FP数学运算,就像所有x86-64编译器一样,以及一些32位编译器,具体取决于选项。旧版x87在寄存器中没有不同的宽度,仅在内存中(它在加载/存储时转换) ),所以历史上甚至sqrt和division对double)来说都很慢。

例如,Haswell的divsd吞吐量为每8到14个周期一个(数据相关),但每7个周期一个divss(标量单个)吞吐量。 x87 fdiv的循环吞吐量为8到18。 (来自https://agner.org/optimize/的数字。延迟与除法的吞吐量相关,但高于吞吐量数字。)

floatlogf(float)等许多库函数的sinf(float)版本也会比log(double)sin(double)更快,因为它们的精确度要低很多。他们可以使用更少项的多项式近似来获得floatdouble的完全精确度


然而,每个数字占用两倍的内存,这明显意味着缓存上更重的负载和更多的内存带宽来填充和溢出这些缓存行/到RAM;你关心浮点运算性能的时间是你正在做很多这样的操作,所以内存和缓存的考虑是至关重要的。

@ Richard的答案指出还有其他方法可以执行FP操作(SSE / SSE2指令;好的旧MMX只有整数),特别适用于大量数据的简单操作(“SIMD” “,单指令/多数据”,其中每个向量寄存器可以包含4个单精度浮点数或仅包含2个双精度浮点数,因此这种效果将更加明显。

最后,您必须进行基准测试,但我的预测是,对于合理的(即大型 ;-)基准测试,您将发现坚持单一精度的优势(当然假设你没有需要额外的精度! - 。)。

答案 1 :(得分:25)

如果在FPU内执行所有浮点计算,那么,不,double计算和float计算之间没有区别,因为浮点运算实际上是用80位执行的FPU堆栈中的精度。 FPU堆栈的条目将根据需要进行舍入,以将80位浮点格式转换为doublefloat浮点格式。将sizeof(double)字节移入/移出RAM与sizeof(float)字节是唯一的速度差异。

但是,如果您有可矢量化计算,则可以使用SSE扩展在两次float计算的同时运行四次double计算。因此,巧妙地使用SSE指令和XMM寄存器可以在仅使用float s的计算中实现更高的吞吐量。

答案 2 :(得分:10)

另一个需要考虑的问题是,如果您使用的是GPU(显卡)。我在一个数字密集的项目中工作,但我们不需要双重提供的精确度。我们使用GPU卡来帮助进一步加快处理速度。 CUDA GPU需要一个特殊的软件包才能支持double,GPU上的本地RAM数量非常快,但非常稀缺。因此,使用float也会使我们可以存储在GPU上的数据量翻倍。

另一点是记忆。浮点数占用RAM的一半。如果您正在处理非常大的数据集,这可能是一个非常重要的因素。如果使用双重意味着您必须缓存到磁盘而不是纯RAM,那么您的差异就会很大。

因此,对于我正在使用的应用程序,差异非常重要。

答案 3 :(得分:10)

在添加3.3次200万次的实验中,结果是:

Summation time in s: 2.82 summed value: 6.71089e+07 // float
Summation time in s: 2.78585 summed value: 6.6e+09 // double
Summation time in s: 2.76812 summed value: 6.6e+09 // long double

因此,在C和C ++中,double更快且默认。它更易于移植,是所有C和C ++库函数的默认值。 Alos double的精度明显高于float。

即使Stroustrup建议翻倍浮动:

“单精度,双精度和扩展精度的确切含义是实现定义的。为选择重要的问题选择合适的精度需要对浮点计算有重要的理解。如果你没有那个理解,获得建议,花时间学习,或使用双重和希望最好。“

也许唯一一种你应该使用float而不是double的情况是64位硬件和现代gcc。因为浮子较小; double是8个字节,float是4个字节。

答案 4 :(得分:10)

我只想补充已经存在的好答案,__m256?系列的同一指令多数据(SIMD)C ++内在函数在上运行 4 double并行(例如_mm256_add_pd)或 8 float s并行(例如_mm256_add_ps

我不确定这是否可以转换为实际加速,但似乎可能在使用SIMD时每条指令处理2x个浮点数。< / p>

答案 5 :(得分:6)

唯一真正有用的答案是:只有你能说出来。您需要对场景进行基准测试。指令和内存模式的微小变化可能会产生重大影响。

如果你使用的是FPU或SSE类型的硬件,那肯定很重要(以前它的所有工作都是80位扩展精度,所以double会更接近;后来本身就是32位,即浮点数。)

更新:s / MMX / SSE /,如另一个答案中所述。

答案 6 :(得分:2)

浮点通常是一个通用CPU的扩展。因此,速度取决于所使用的硬件平台。如果平台有浮点支持,如果有任何不同,我会感到惊讶。

答案 7 :(得分:-1)

另外还可以查看一些基准的真实数据:

For Intel 3770k, GCC 9.3.0 -O2 [3]
Run on (8 X 3503 MHz CPU s)
CPU Caches:
  L1 Data 32 KiB (x4)
  L1 Instruction 32 KiB (x4)
  L2 Unified 256 KiB (x4)
  L3 Unified 8192 KiB (x1)
--------------------------------------------------------------------
Benchmark                          Time             CPU   Iterations
--------------------------------------------------------------------
BM_FloatCreation               0.281 ns        0.281 ns   1000000000
BM_DoubleCreation              0.284 ns        0.281 ns   1000000000
BM_Vector3FCopy                0.558 ns        0.562 ns   1000000000
BM_Vector3DCopy                 5.61 ns         5.62 ns    100000000
BM_Vector3F_CopyDefault        0.560 ns        0.546 ns   1000000000
BM_Vector3D_CopyDefault         5.57 ns         5.56 ns    112178768
BM_Vector3F_Copy123            0.841 ns        0.817 ns    897430145
BM_Vector3D_Copy123             5.59 ns         5.42 ns    112178768
BM_Vector3F_Add                0.841 ns        0.834 ns    897430145
BM_Vector3D_Add                 5.59 ns         5.46 ns    100000000
BM_Vector3F_Mul                0.842 ns        0.782 ns    897430145
BM_Vector3D_Mul                 5.60 ns         5.56 ns    112178768
BM_Vector3F_Compare            0.840 ns        0.800 ns    897430145
BM_Vector3D_Compare             5.61 ns         5.62 ns    100000000
BM_Vector3F_ARRAY_ADD           3.25 ns         3.29 ns    213673844        
BM_Vector3D_ARRAY_ADD           3.13 ns         3.06 ns    224357536        

比较3个float(F)或3个double(D)上的运算, -BM_Vector3XCopy是(1,2,3)初始化向量的纯副本,在复制前不重复, -BM_Vector3X_CopyDefault,每个副本重复默认初始化, -重复初始化(1,2,3)的BM_Vector3X_Copy123,

  • 加/乘每个初始化3个向量(1,2,3),并将第一个和第二个加/乘到第三个,
  • 比较检查两个初始化向量是否相等

  • ARRAY_ADD通过std :: valarray汇总vector(1,2,3)+ vector(3,4,5)+ vector(6,7,8)在我的情况下会导致SSE指令。 / p>

请记住,这些是孤立的测试,并且结果因编译器设置的不同而异,从机器到机器或从体系结构到体系结构。 对于缓存(问题)和实际用例,这可能是完全不同的。因此,该理论可能与现实大相径庭。 找出答案的唯一方法是进行实际测试,例如使用google-benchmark [1]并检查特定问题解决方案的编译器输出结果[2]。

  1. https://github.com/google/benchmark
  2. https://sourceware.org/binutils/docs/binutils/objdump.html-> objdump -S
  3. https://github.com/Jedzia/oglTemplate/blob/dd812b72d846ae888238d6f726d503485b796b68/benchmark/Playground/BM_FloatingPoint.cpp