为什么在 Float64 上运行比 Float16 快?

Sha*_*yan 38 performance julia half-precision-float

我想知道为什么对Float64值进行操作比对 进行操作更快Float16

\n
julia> rnd64 = rand(Float64, 1000);\n\njulia> rnd16 = rand(Float16, 1000);\n\njulia> @benchmark rnd64.^2\nBenchmarkTools.Trial: 10000 samples with 10 evaluations.\n Range (min \xe2\x80\xa6 max):  1.800 \xce\xbcs \xe2\x80\xa6 662.140 \xce\xbcs  \xe2\x94\x8a GC (min \xe2\x80\xa6 max):  0.00% \xe2\x80\xa6 99.37%\n Time  (median):     2.180 \xce\xbcs               \xe2\x94\x8a GC (median):     0.00%\n Time  (mean \xc2\xb1 \xcf\x83):   3.457 \xce\xbcs \xc2\xb1  13.176 \xce\xbcs  \xe2\x94\x8a GC (mean \xc2\xb1 \xcf\x83):  12.34% \xc2\xb1  3.89%\n\n  \xe2\x96\x81\xe2\x96\x88\xe2\x96\x88\xe2\x96\x84\xe2\x96\x82\xe2\x96\x82\xe2\x96\x86\xe2\x96\x86\xe2\x96\x84\xe2\x96\x82\xe2\x96\x81 \xe2\x96\x82\xe2\x96\x86\xe2\x96\x84\xe2\x96\x81                                     \xe2\x96\x82\xe2\x96\x82\xe2\x96\x82\xe2\x96\x81   \xe2\x96\x82\n  \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x87\xe2\x96\x87\xe2\x96\x86\xe2\x96\x86\xe2\x96\x87\xe2\x96\x86\xe2\x96\x85\xe2\x96\x87\xe2\x96\x88\xe2\x96\x88\xe2\x96\x86\xe2\x96\x86\xe2\x96\x85\xe2\x96\x85\xe2\x96\x86\xe2\x96\x84\xe2\x96\x84\xe2\x96\x81\xe2\x96\x81\xe2\x96\x83\xe2\x96\x83\xe2\x96\x81\xe2\x96\x81\xe2\x96\x84\xe2\x96\x81\xe2\x96\x83\xe2\x96\x84\xe2\x96\x81\xe2\x96\x83\xe2\x96\x81\xe2\x96\x84\xe2\x96\x83\xe2\x96\x81\xe2\x96\x81\xe2\x96\x86\xe2\x96\x87\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x87 \xe2\x96\x88\n  1.8 \xce\xbcs       Histogram: log(frequency) by time      10.6 \xce\xbcs <\n\n Memory estimate: 8.02 KiB, allocs estimate: 5.\n\njulia> @benchmark rnd16.^2\nBenchmarkTools.Trial: 10000 samples with 6 evaluations.\n Range (min \xe2\x80\xa6 max):  5.117 \xce\xbcs \xe2\x80\xa6 587.133 \xce\xbcs  \xe2\x94\x8a GC (min \xe2\x80\xa6 max): 0.00% \xe2\x80\xa6 98.61%\n Time  (median):     5.383 \xce\xbcs               \xe2\x94\x8a GC (median):    0.00%\n Time  (mean \xc2\xb1 \xcf\x83):   5.716 \xce\xbcs \xc2\xb1   9.987 \xce\xbcs  \xe2\x94\x8a GC (mean \xc2\xb1 \xcf\x83):  3.01% \xc2\xb1  1.71%\n\n    \xe2\x96\x83\xe2\x96\x85\xe2\x96\x88\xe2\x96\x87\xe2\x96\x85\xe2\x96\x84\xe2\x96\x84\xe2\x96\x86\xe2\x96\x87\xe2\x96\x85\xe2\x96\x84\xe2\x96\x81             \xe2\x96\x81                                \xe2\x96\x82\n  \xe2\x96\x84\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x87\xe2\x96\x86\xe2\x96\x87\xe2\x96\x86\xe2\x96\x86\xe2\x96\x87\xe2\x96\x86\xe2\x96\x87\xe2\x96\x85\xe2\x96\x88\xe2\x96\x87\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x87\xe2\x96\x88\xe2\x96\x87\xe2\x96\x87\xe2\x96\x86\xe2\x96\x85\xe2\x96\x86\xe2\x96\x84\xe2\x96\x87\xe2\x96\x87\xe2\x96\x86\xe2\x96\x88\xe2\x96\x87\xe2\x96\x88\xe2\x96\x88\xe2\x96\x87\xe2\x96\x88\xe2\x96\x87\xe2\x96\x87\xe2\x96\x87\xe2\x96\x86\xe2\x96\x87\xe2\x96\x87\xe2\x96\x86\xe2\x96\x86\xe2\x96\x86\xe2\x96\x86\xe2\x96\x84\xe2\x96\x84 \xe2\x96\x88\n  5.12 \xce\xbcs      Histogram: log(frequency) by time      7.48 \xce\xbcs <\n\n Memory estimate: 2.14 KiB, allocs estimate: 5.\n
Run Code Online (Sandbox Code Playgroud)\n

也许您会问为什么我期望相反的结果:因为Float16值的浮点精度较低:

\n
julia> rnd16[1]\nFloat16(0.627)\n\njulia> rnd64[1]\n0.4375452455597999\n
Run Code Online (Sandbox Code Playgroud)\n

精度较低的计算不应该更快吗?然后我想知道为什么有人应该使用Float16?他们甚至可以做到Float128

\n

DNF*_*DNF 47

正如您所看到的,您所期望的效果已经出现Float32

\n
julia> rnd64 = rand(Float64, 1000);\n\njulia> rnd32 = rand(Float32, 1000);\n\njulia> rnd16 = rand(Float16, 1000);\n\njulia> @btime $rnd64.^2;\n  616.495 ns (1 allocation: 7.94 KiB)\n\njulia> @btime $rnd32.^2;\n  330.769 ns (1 allocation: 4.06 KiB)  # faster!!\n\njulia> @btime $rnd16.^2;\n  2.067 \xce\xbcs (1 allocation: 2.06 KiB)  # slower!!\n
Run Code Online (Sandbox Code Playgroud)\n

Float64并且Float32在大多数平台上都有硬件支持,但是Float16没有,因此必须用软件实现。

\n

另请注意,您应该使用变量插值($另请注意,微基准测试时应这里的差异是显着的,尤其是在分配方面:

\n
julia> @btime $rnd32.^2;\n  336.187 ns (1 allocation: 4.06 KiB)\n\njulia> @btime rnd32.^2;\n  930.000 ns (5 allocations: 4.14 KiB)\n
Run Code Online (Sandbox Code Playgroud)\n


Osc*_*ith 20

简而言之,除非您使用 GPU 或 Apple CPU,否则您可能不应该使用 Float16,因为(截至 2022 年)其他处理器不具备对 Float16 的硬件支持。

  • 不完全正确的是,没有其他 CPU 具有支持:未锁定 AVX-512 的 Alder Lake 具有 AVX512-FP16,可以为 FP16(不仅仅是 BF16)提供标量和打包 SIMD 支持。还有 Sapphire Rapids Xeon,尽管尚未正式推出。有关 CPU 扩展表,请参阅 https://en.wikipedia.org/wiki/AVX-512#CPUs_with_AVX-512。以及[Intel芯片上的半精度浮点运算](/sf/ask/3499691611/)。但是,是的,在 2023 年之前推出的主流 x86 CPU 都没有在 CPU 上正式支持 FP16,只有 iGPU。 (13认同)
  • @JUL:9 年前也不存在支持。 (4认同)
  • 我不会说您“不应该”在其他硬件上使用 Float16。在特殊情况下,您正在进行大量数字运算,并且不需要大于 65504 的数字,不需要超过 3 个小数位的精度,并且不需要最大化 CPU 速度,*但是*您拥有这些数字的*大量*数组并且内存非常宝贵,那么使用 Float16 将是一个有用的优化。OTOH,如果您不需要大量内存但确实需要速度或准确性,请使用 Float64。 (3认同)