Це своєрідне порівняння ефективності, оскільки зазвичай вимірюється час, необхідний для обчислення чогось істотного, а не бачення, скільки тривіальних ітерацій можна виконати за певний проміжок часу. У мене виникли проблеми з тим, щоб ваші коди 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
}
Компілятор бачить цикл, з'ясовує, що результат щоразу однаковий, і просто повертає це постійне значення, а не виконувати цикл. Що, звичайно, займає нуль часу.