Это своего рода странное сравнение производительности, поскольку обычно измеряется время, необходимое для вычисления чего-то существенного, вместо того, чтобы видеть, сколько тривиальных итераций можно выполнить за определенное время. У меня были проблемы с тем, чтобы заставить работать ваши коды Python и Julia, поэтому я изменил код Julia для работы и просто не запустил код Python. Как отметил @chepner в комментарии, использование now()
и сравнение времени с DateTime
объектами довольно дорого. Функция Python time.time()
просто возвращает значение с плавающей точкой. Оказывается, есть функция Julia, time()
которая делает то же самое:
julia> time()
1.587648091474481e9
Вот время вашей исходной f()
функции (модифицированной для работы) в моей системе:
julia> using Dates
julia> function f()
i = 0
t1 = now()
while true
i += 1
if now() - t1 >= Millisecond(1000)
break
end
end
return i
end
f (generic function with 1 method)
julia> f()
4943739
Он сделал почти 5 миллионов итераций, прежде чем время истекло. Как я уже сказал, я не смог заставить ваш Python-код работать в моей системе без значительных хлопот (что я и не удосужился сделать). Но вот версия, f()
которая использует time()
вместо этого, что я буду образно называть g()
:
julia> function g()
i = 0
t1 = time()
while true
i += 1
if time() - t1 >= 1
break
end
end
return i
end
g (generic function with 1 method)
julia> g()
36087637
Эта версия сделала 36 миллионов итераций. Значит, Джулия быстрее зацикливается? Ура! Ну, на самом деле основная работа в этом цикле - это вызовы time()
так ... Джулия быстрее генерирует много time()
вызовов!
Почему странно это время? Как я уже сказал, большая часть реальной работы здесь зовет time()
. Остальная часть цикла ничего не делает. В оптимизируемом скомпилированном языке, если компилятор видит цикл, который ничего не делает, он полностью его устраняет. Например:
julia> function h()
t = 0
for i = 1:100_000_000
t += i
end
return t
end
h (generic function with 1 method)
julia> h()
5000000050000000
julia> @time h()
0.000000 seconds
5000000050000000
Вау, ноль секунд! Как это возможно? Что ж, давайте посмотрим на код LLVM (вроде машинного кода, но для воображаемой машины, которая используется в качестве промежуточного представления), это понижается до:
julia> @code_llvm h()
; @ REPL[16]:1 within `h'
define i64 @julia_h_293() {
top:
; @ REPL[16]:6 within `h'
ret i64 5000000050000000
}
Компилятор видит цикл, выясняет, что результат каждый раз один и тот же, и просто возвращает это постоянное значение вместо фактического выполнения цикла. Что, конечно, занимает нулевое время.