Для скаляров оператор \
(решить линейную систему) эквивалентен оператору деления /
. Производительность похожа?
Я спрашиваю, потому что в настоящее время в моем коде есть строка вроде
x = (1 / alpha) * averylongfunctionname(input1, input2, input3)
Визуально важно, чтобы деление на alpha
происходило слева, поэтому я рассматриваю возможность замены этого на
x = alpha \ averylongfunctionname(input1, input2, input3)
Какова наилучшая практика в этой ситуации с точки зрения стиля и производительности?
Вот некоторые озадачивающие результаты бенчмаркинга:
julia> using BenchmarkTools
[ Info: Precompiling BenchmarkTools [6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf]
julia> @btime x[1]\sum(x) setup=(x=rand(100))
15.014 ns (0 allocations: 0 bytes)
56.23358979466163
julia> @btime (1/x[1]) * sum(x) setup=(x=rand(100))
13.312 ns (0 allocations: 0 bytes)
257.4552413802698
julia> @btime sum(x)/x[1] setup=(x=rand(100))
14.929 ns (0 allocations: 0 bytes)
46.25209548841374
Они все примерно одинаковые, но я удивлен, что подход (1 / x) * foo
имеет лучшую производительность.
Я не знаю, но я могу предложить вам попробовать использовать BenchmarkTools пакет: он может помочь вам оценить производительность двух разные утверждения. Здесь вы можете найти более подробную информацию. Пока!
Я думаю, что лучший выбор (1/x)*foo
по двум причинам:
Скалярные /
и \
действительно должны иметь одинаковое значение и производительность. Давайте определим эти две тестовые функции:
f(a, b) = a / b
g(a, b) = b \ a
Затем мы видим, что они производят идентичный код LLVM:
julia> @code_llvm f(1.5, 2.5)
; @ REPL[29]:1 within `f'
define double @julia_f_380(double %0, double %1) {
top:
; ┌ @ float.jl:335 within `/'
%2 = fdiv double %0, %1
; └
ret double %2
}
julia> @code_llvm g(1.5, 2.5)
; @ REPL[30]:1 within `g'
define double @julia_g_382(double %0, double %1) {
top:
; ┌ @ operators.jl:579 within `\'
; │┌ @ float.jl:335 within `/'
%2 = fdiv double %0, %1
; └└
ret double %2
}
И тот же машинный код тоже. Я не уверен, что вызывает различия в результатах @btime
, но я почти уверен, что разница между /
и \
— это иллюзия, а не реальность.
Что касается x*(1/y)
, это не вычисляет то же самое, что и x/y
: потенциально оно будет менее точным, поскольку при вычислении 1/y
выполняется округление, а затем это округленное значение умножается на x
, что также округляется. Например:
julia> 17/0.7
24.28571428571429
julia> 17*(1/0.7)
24.285714285714285
Поскольку деление с плавающей запятой гарантированно будет правильно округлено, прямое деление всегда будет более точным. Однако, если делитель используется многими итерациями цикла, вы можете получить ускорение, переписав вычисление таким образом, поскольку умножение с плавающей запятой обычно быстрее, чем деление (хотя мой текущий компьютер не показывает это). Имейте в виду, однако, что это приводит к потере точности, и если делитель не является общим, все равно будет потеря точности и никакого прироста производительности.
Мне нравится использовать alpha \ (longer expression)
, но некоторых это может сбить с толку, поэтому это кажется немного субъективным. Я бы сказал, используйте его, если он вам нравится, и вы найдете его более ясным.
Спасибо. Есть ли у вас мысли по поводу стиля?