Интересно, почему работа со значениями Float64 выполняется быстрее, чем работа с Float16:
julia> rnd64 = rand(Float64, 1000);
julia> rnd16 = rand(Float16, 1000);
julia> @benchmark rnd64.^2
BenchmarkTools.Trial: 10000 samples with 10 evaluations.
Range (min … max): 1.800 μs … 662.140 μs ┊ GC (min … max): 0.00% … 99.37%
Time (median): 2.180 μs ┊ GC (median): 0.00%
Time (mean ± σ): 3.457 μs ± 13.176 μs ┊ GC (mean ± σ): 12.34% ± 3.89%
▁██▄▂▂▆▆▄▂▁ ▂▆▄▁ ▂▂▂▁ ▂
████████████████▇▇▆▆▇▆▅▇██▆▆▅▅▆▄▄▁▁▃▃▁▁▄▁▃▄▁▃▁▄▃▁▁▆▇██████▇ █
1.8 μs Histogram: log(frequency) by time 10.6 μs <
Memory estimate: 8.02 KiB, allocs estimate: 5.
julia> @benchmark rnd16.^2
BenchmarkTools.Trial: 10000 samples with 6 evaluations.
Range (min … max): 5.117 μs … 587.133 μs ┊ GC (min … max): 0.00% … 98.61%
Time (median): 5.383 μs ┊ GC (median): 0.00%
Time (mean ± σ): 5.716 μs ± 9.987 μs ┊ GC (mean ± σ): 3.01% ± 1.71%
▃▅█▇▅▄▄▆▇▅▄▁ ▁ ▂
▄██████████████▇▆▇▆▆▇▆▇▅█▇████▇█▇▇▆▅▆▄▇▇▆█▇██▇█▇▇▇▆▇▇▆▆▆▆▄▄ █
5.12 μs Histogram: log(frequency) by time 7.48 μs <
Memory estimate: 2.14 KiB, allocs estimate: 5.
Может быть, вы спросите, почему я ожидаю обратного: потому что значения Float16 имеют меньшую точность с плавающей запятой:
julia> rnd16[1]
Float16(0.627)
julia> rnd64[1]
0.4375452455597999
Разве расчеты с меньшей точностью не должны выполняться быстрее? Тогда мне интересно, почему кто-то должен использовать Float16? Они могут сделать это даже с Float128!
@mcabbott, я мысленно догадался о возможности преобразования. Большое спасибо!
Какой процессор у вас есть? Если x86, то есть ли в нем AVX512-FP16 для прямой поддержки fp16 без конвертации, скаляра и SIMD? (Sapphire Rapids и новее и, возможно, Alder Lake с разблокированным AVX-512, к сожалению, не Zen 4.) Если нет, то большинство процессоров x86 за последнее десятилетие имеют инструкции для упакованного преобразования между fp16 и fp32, но это все. Арифметика с плавающей запятой половинной точности на чипах Intel. Если в вашем процессоре даже нет F16C, для преобразования потребуется несколько инструкций.
Поплавки половинной точности часто используются для экономии памяти, а не скорости.


Короткий ответ заключается в том, что вам, вероятно, не следует использовать Float16, если вы не используете графический процессор или процессор Apple, потому что (по состоянию на 2022 год) другие процессоры не имеют аппаратной поддержки Float16.
В связи с вашим обновлением: Моя машина относится к 2013 году.
@JUL: 9 лет назад поддержки тоже не было.
Не совсем верно, что никакие другие процессоры не поддерживают: Alder Lake с разблокированным AVX-512 имеет AVX512-FP16 для скалярной и упакованной поддержки SIMD для FP16 (а не только BF16). Также Sapphire Rapids Xeon, хотя официально он еще не запущен. См. en.wikipedia.org/wiki/AVX-512#CPUs_with_AVX-512 таблицу расширений по ЦП. И Арифметика с плавающей запятой половинной точности на чипах Intel. Но да, ни один из основных процессоров x86 с датой запуска до 2023 года официально не поддерживает FP16 на процессоре, только iGPU.
Я бы не сказал, что вы не должны использовать Float16 на другом оборудовании. В особых случаях, когда вы выполняете кучу операций с числами и не требуете чисел больше 65504, не требуете точности более 3 десятичных цифр и не требуете максимальной скорости процессора, но у вас есть огромные массивы этих чисел и память в большом почете, то использование Float16 было бы полезной оптимизацией. OTOH, если вам не нужно много памяти, но нужна скорость или точность, используйте Float64.
Да, технически есть места, где это может быть полезно, но обычно есть какая-то другая форма потребления памяти, которая в этот момент будет быстрее.
Как видите, ожидаемый эффект присутствует для Float32:
julia> rnd64 = rand(Float64, 1000);
julia> rnd32 = rand(Float32, 1000);
julia> rnd16 = rand(Float16, 1000);
julia> @btime $rnd64.^2;
616.495 ns (1 allocation: 7.94 KiB)
julia> @btime $rnd32.^2;
330.769 ns (1 allocation: 4.06 KiB) # faster!!
julia> @btime $rnd16.^2;
2.067 μs (1 allocation: 2.06 KiB) # slower!!
Float64 и Float32 имеют аппаратную поддержку на большинстве платформ, а Float16 нет, и поэтому должны быть реализованы программно.
Также обратите внимание, что при микротестировании следует использовать переменную интерполяцию ($). Разница существенна здесь, не в последнюю очередь с точки зрения ассигнований:
julia> @btime $rnd32.^2;
336.187 ns (1 allocation: 4.06 KiB)
julia> @btime rnd32.^2;
930.000 ns (5 allocations: 4.14 KiB)
Большой! А как насчет возможности конвертации?stackoverflow.com/questions/74703898/…
x86, так как у Ivy Bridge есть аппаратная поддержка преобразования между FP16 и FP32, VCVTPH2PS YMM, XMM или VCVTPH2PS YMM, mem по-прежнему составляет 2 мкп на Intel. И обратное преобразование с памятью или регистром назначения составляет 4 или 3 мопов на Haswell (это может быть процессор OP 2013 или может быть Ivy Bridge). Если мопы преобразования также конкурируют за ограниченные внутренние порты, порт 1 оба направления на Ivy Bridge и Haswell, а также порт случайного воспроизведения (порт 5), за исключением версии с источником памяти. Это инструкция AVX; IDK, если бы Джулия использовала его автоматически.
Есть аппаратная поддержка 32 и 64, но я думаю, что Float16 конвертируется перед большинством операций: docs.julialang.org/en/v1/manual/… . На процессорах ARM (например, M1 mac) есть некоторая поддержка, например.
@btime $(similar(rnd16)) .= 2 .* $rnd16;быстрее, чем 64. Это совсем недавно, см., например. github.com/JuliaLang/julia/issues/40216