Swift 中的双倍 vs Float80 速度
Double vs Float80 speed in Swift
我听说x87 FPU是80位浮点数,所以即使我想用64位数字计算,它也会用80位计算然后转换。但是 Swift 在 x86-64、Double
或 Float80
中哪个最快(计算算术时)?
虽然 x87 FPU 确实以 80 位 "extended" 精度在内部运行(至少,默认情况下;这是可定制的,实际上 32 位构建遵循 macOS ABI 集 64 位内部精度),针对 x86-64 的二进制文件不再使用 x87 FPU 指令。所有实现 64 位长模式扩展的 x86 芯片也支持 SSE2(事实上,这是 AMD64 规范所要求的),因此 64 位二进制文件始终可以假设支持 SSE2。因此,这就是用于实现浮点运算的方法,因为它更高效,也更容易为编译器优化。
即使是现代的 32 位构建也将 SSE2 视为最低限度,并且 当然 在 Macintosh 平台上,因为 SSE2 是在 Pentium 4 中引入的,它早于 Macintosh平台切换到 Intel x86 芯片。 Apple 机器中使用过的所有 x86 芯片都支持 SSE2。
所以不,您不会通过使用 80 位扩展精度类型看到任何性能改进。您不会从 x87 指令中看到任何性能改进,即使它们是由编译器生成的。而且您肯定不会在 x86-64 上看到任何性能改进,因为 SSE2 在硬件中支持最大 64 位精度。任何 80 位精度的操作都必须在软件中实现,或者强制智能编译器发出 x87 指令,这意味着您无法从 SSE2 的任何好的特性和有形的性能改进中受益。
Double
几乎总是[1] 在现代英特尔处理器上的 Float80
上至少与几乎任何语言一样快。在某些情况下它会明显更快:
Double
使用更少的内存;使用 Double
时算法的工作集可能适合缓存,但使用 Float80
时无法适合,从而导致严重的性能危害。
Double
可以利用 FMA 指令(在 Swift 中公开为 .add[ing]Product(x,y)
和 fma()
自由函数),这有效地使最近的内核可达到的浮点吞吐量。
Double
可以由编译器自动矢量化。 Float80
上没有矢量指令。在可能的情况下,这可以为您提供高达 4 倍的加速。
sin
、cos
、pow
等数学函数在 Double
上比在 Float80
上更快.
还有其他一些使用 Double
的原因:它可以移植到非 x86 硬件,而 Float80
则不能,并且 Double
与 C 接口的互操作性比 Double
更容易Float80
。您应该只在必要时使用 Float80
,否则默认使用 Double
。
[1] 有一些小众案例 Float80
可以更快——如果算法在 Double
中反复下溢,但在 Float80
中保持在正常范围内,因为例子。这些很少见,通常不值得担心;更常见的是,您的算法也会在 Float80
中下溢,只需稍后进行几次迭代即可。
我听说x87 FPU是80位浮点数,所以即使我想用64位数字计算,它也会用80位计算然后转换。但是 Swift 在 x86-64、Double
或 Float80
中哪个最快(计算算术时)?
虽然 x87 FPU 确实以 80 位 "extended" 精度在内部运行(至少,默认情况下;这是可定制的,实际上 32 位构建遵循 macOS ABI 集 64 位内部精度),针对 x86-64 的二进制文件不再使用 x87 FPU 指令。所有实现 64 位长模式扩展的 x86 芯片也支持 SSE2(事实上,这是 AMD64 规范所要求的),因此 64 位二进制文件始终可以假设支持 SSE2。因此,这就是用于实现浮点运算的方法,因为它更高效,也更容易为编译器优化。
即使是现代的 32 位构建也将 SSE2 视为最低限度,并且 当然 在 Macintosh 平台上,因为 SSE2 是在 Pentium 4 中引入的,它早于 Macintosh平台切换到 Intel x86 芯片。 Apple 机器中使用过的所有 x86 芯片都支持 SSE2。
所以不,您不会通过使用 80 位扩展精度类型看到任何性能改进。您不会从 x87 指令中看到任何性能改进,即使它们是由编译器生成的。而且您肯定不会在 x86-64 上看到任何性能改进,因为 SSE2 在硬件中支持最大 64 位精度。任何 80 位精度的操作都必须在软件中实现,或者强制智能编译器发出 x87 指令,这意味着您无法从 SSE2 的任何好的特性和有形的性能改进中受益。
Double
几乎总是[1] 在现代英特尔处理器上的 Float80
上至少与几乎任何语言一样快。在某些情况下它会明显更快:
Double
使用更少的内存;使用Double
时算法的工作集可能适合缓存,但使用Float80
时无法适合,从而导致严重的性能危害。Double
可以利用 FMA 指令(在 Swift 中公开为.add[ing]Product(x,y)
和fma()
自由函数),这有效地使最近的内核可达到的浮点吞吐量。Double
可以由编译器自动矢量化。Float80
上没有矢量指令。在可能的情况下,这可以为您提供高达 4 倍的加速。sin
、cos
、pow
等数学函数在Double
上比在Float80
上更快.
还有其他一些使用 Double
的原因:它可以移植到非 x86 硬件,而 Float80
则不能,并且 Double
与 C 接口的互操作性比 Double
更容易Float80
。您应该只在必要时使用 Float80
,否则默认使用 Double
。
[1] 有一些小众案例 Float80
可以更快——如果算法在 Double
中反复下溢,但在 Float80
中保持在正常范围内,因为例子。这些很少见,通常不值得担心;更常见的是,您的算法也会在 Float80
中下溢,只需稍后进行几次迭代即可。