使用比浮动更快的双倍?

Bre*_*ust 66 c++ performance x86 intel osx-snow-leopard

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

也就是说,双重操作与+, - ,*和/的浮点运算一样快或快.

对于64位架构,答案是否会改变?

Ale*_*lli 73

There isn't a single "intel CPU", especially in terms of what operations are optimized with respect to others!, but most of them, at CPU level (specifically within the FPU), are such that the answer to your question:

are double operations just as fast or faster than float operations for +, -, *, and /?

is "yes" -- within the CPU, except for division and sqrt which are somewhat slower for double than for float. (Assuming your compiler uses SSE2 for scalar FP math, like all x86-64 compilers do, and some 32-bit compilers depending on options. Legacy x87 doesn't have different widths in registers, only in memory (it converts on load/store), so historically even sqrt and division were just as slow for double).

For example, Haswell has a divsd throughput of one per 8 to 14 cycles (data-dependent), but a divss (scalar single) throughput of one per 7 cycles. x87 fdiv is 8 to 18 cycle throughput. (Numbers from https://agner.org/optimize/. Latency correlates with throughput for division, but is higher than the throughput numbers.)

The float versions of many library functions like logf(float) and sinf(float) will also be faster than log(double) and sin(double), because they have many fewer bits of precision to get right. They can use polynomial approximations with fewer terms to get full precision for float vs. double


However, taking up twice the memory for each number clearly implies heavier load on the cache(s) and more memory bandwidth to fill and spill those cache lines from/to RAM; the time you care about performance of a floating-point operation is when you're doing a lot of such operations, so the memory and cache considerations are crucial.

@Richard's answer points out that there are also other ways to perform FP operations (the SSE/SSE2 instructions; good old MMX was integers-only), especially suitable for simple ops on lot of data ("SIMD", single instruction/multiple data) where each vector register can pack 4 single-precision floats or only 2 double-precision ones, so this effect will be even more marked.

最后,你必须进行基准测试,但我的预测是,对于合理的(即大的 ;-)基准测试,你会发现坚持单一精度的优势(当然假设你不需要额外的位数)精确!-).

  • @Razor如果你在高速缓存行中使用的浮动数与浮点数一样多,那么如果使用双精度数,则CPU必须获取两个高速缓存行.然而,当我阅读Alex的答案时,我想到的缓存效果是:你的浮动集合适合你的第n级缓存,但相应的双精度集合却没有.在这种情况下,如果使用浮动,您将体验到性能的巨大提升. (4认同)

Dan*_*ien 24

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

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


Mil*_*ley 11

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

另一点是记忆.浮点数占用RAM的一半.如果您正在处理非常大的数据集,这可能是一个非常重要的因素.如果使用双重意味着你必须缓存到磁盘与纯ram,你的差异将是巨大的.

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


bob*_*obo 10

我只想补充到现有的伟大的答案是,__m256?家庭同指令多数据(SIMD)C++内部函数进行操作或者 4个 double S IN并行(例如_mm256_add_pd),或8个 float S IN并行(例如_mm256_add_ps).

我不确定这是否可以转化为实际的加速,但是当使用SIMD时,每条指令可以处理2 倍的浮点数.


Aka*_*wal 9

在添加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
Run Code Online (Sandbox Code Playgroud)

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

甚至Stroustrup建议浮动双倍:

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

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

  • 当他实际推荐RTFM时,Stroustrup建议在那里推荐"双倍",这是一个很大的延伸. (4认同)
  • +1用于努力做一些时间.但是Stroustrup并不建议使用'double',因为它更快,但是由于额外的精度.关于你的最后评论,如果你需要额外的精度而不是节省内存,那么你很可能想在32位硬件上使用'double'.这又引出了一个问题:即使在具有64位计算功能的现代FPU的32位硬件上,浮动速度是否比浮动速度快? (3认同)

Ric*_*ard 7

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

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

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


小智 5

Alex Martelli的答案已经足够好了,但我想提一个错误但有点流行的测试方法,可能会误导一些人:

#include <cstdio>
#include <ctime>
int main() {
  const auto start_clock = clock();
  float a = 0;
  for (int i = 0; i < 256000000; i++) {
    // bad latency benchmark that includes as much division as other operations
    a += 0.11;  // note the implicit conversions of a to double to match 0.11
    a -= 0.13;  // rather than 0.11f
    a *= 0.17;
    a /= 0.19;
  }
  printf("c++ float duration = %.3f\n", 
    (double)(clock() - start_clock) / CLOCKS_PER_SEC);
  printf("%.3f\n", a);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是不对的!C++ 默认使用double,如果替换+= 0.11+= 0.11f,在 x86 CPU 上,float 通常会比 double 更快。

顺便说一句,在现代 SSE 指令集上,除了除法运算之外,在 CPU 内核本身中,浮点型和双精度型具有相同的速度。 float如果你有数组的话,越小可能会有更少的缓存未命中。

如果编译器可以自动向量化,则浮点向量在每条指令上处理的元素数量是双精度向量的两倍。