Это своего рода странное сравнение производительности, поскольку обычно измеряется время, необходимое для вычисления чего-то существенного, вместо того, чтобы видеть, сколько тривиальных итераций можно выполнить за определенное время. У меня были проблемы с тем, чтобы заставить работать ваши 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 миллионов итераций. Значит, Джулия быстрее зацикливается? Ура! Ну, на самом деле основная работа в этом l oop - это звонки на time()
, так что ... Джулия быстрее генерирует множество time()
звонков!
Почему это странное время? Как я уже сказал, большая часть реальной работы здесь называется time()
. Остальная часть l oop ничего не делает. В оптимизируемом скомпилированном языке, если компилятор видит, что oop ничего не делает, он полностью его устраняет. Например:
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
}
Компилятор видит l oop, выясняет, что результат всегда один и тот же, и просто возвращает это постоянное значение вместо фактического выполнения l oop. Что, конечно, занимает ноль времени.