Đây là một so sánh hiệu suất kỳ lạ vì thông thường người ta đo thời gian cần thiết để tính toán một thứ gì đó, thay vì xem có bao nhiêu lần lặp lại tầm thường mà người ta có thể làm trong một khoảng thời gian nhất định. Tôi gặp sự cố khi mã Python và Julia của bạn hoạt động, vì vậy tôi đã sửa đổi mã Julia để hoạt động và chỉ không chạy mã Python. Theo ghi nhận của @chepner trong một bình luận, sử dụng now()
và thực hiện so sánh thời gian với DateTime
các đối tượng là khá tốn kém. Hàm Python time.time()
chỉ trả về giá trị dấu phẩy động. Hóa ra, có một hàm Julia được gọi là time()
thực hiện chính xác điều tương tự:
julia> time()
1.587648091474481e9
Đây là thời gian của f()
chức năng ban đầu của bạn (được sửa đổi để hoạt động) trên hệ thống của tôi:
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
Nó đã thực hiện gần 5 triệu lần lặp trước khi hết giờ. Như tôi đã nói, tôi không thể có được mã Python của bạn để chạy trên hệ thống của mình mà không gặp khó khăn gì (điều mà tôi không bận tâm làm). Nhưng đây là phiên bản f()
sử dụng time()
thay thế, mà tôi sẽ gọi một cách tưởng tượng 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
Phiên bản này đã làm 36 triệu lần lặp. Vì vậy, tôi đoán Julia là nhanh hơn trong vòng lặp? Yay! Chà, thực ra công việc chính trong vòng lặp này là các cuộc gọi để time()
... Julia nhanh hơn trong việc tạo ra nhiều time()
cuộc gọi!
Tại sao nó kỳ lạ với thời gian này? Như tôi đã nói, hầu hết các công việc thực tế ở đây là gọi time()
. Phần còn lại của vòng lặp không thực sự làm gì cả. Trong một ngôn ngữ được biên dịch tối ưu hóa, nếu trình biên dịch thấy một vòng lặp không làm gì cả, nó sẽ loại bỏ nó hoàn toàn. Ví dụ:
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
Ái chà, không giây! Làm thế nào là có thể? Chà, hãy xem mã LLVM (giống như mã máy nhưng đối với một máy tưởng tượng được sử dụng làm đại diện trung gian) thì điều này hạ thấp xuống:
julia> @code_llvm h()
; @ REPL[16]:1 within `h'
define i64 @julia_h_293() {
top:
; @ REPL[16]:6 within `h'
ret i64 5000000050000000
}
Trình biên dịch nhìn thấy vòng lặp, chỉ ra rằng kết quả là giống nhau mọi lúc và chỉ trả về giá trị không đổi đó thay vì thực sự thực hiện vòng lặp. Mà, tất nhiên, mất không thời gian.