Esta es una especie de comparación de rendimiento extraño, ya que normalmente se mide el tiempo que lleva calcular algo sustancial, en lugar de ver cuántas iteraciones triviales se pueden hacer en una cierta cantidad de tiempo. Tuve problemas para que sus códigos Python y Julia funcionen, así que modifiqué el código Julia para que funcione y simplemente no ejecuté el código Python. Como señaló @chepner en un comentario, usar now()
y hacer comparaciones de tiempo con DateTime
objetos es bastante costoso. La time.time()
función Python solo devuelve un valor de punto flotante. Como resultado, hay una función llamada Julia time()
que hace exactamente lo mismo:
julia> time()
1.587648091474481e9
Aquí está el momento de su f()
función original (modificada para trabajar) en mi sistema:
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
Hizo casi 5 millones de iteraciones antes de que se agotara el tiempo. Como dije, no pude hacer que tu código de Python se ejecutara en mi sistema sin una manipulación significativa (lo cual no me molesté en hacer). Pero aquí hay una versión de f()
esos usos time()
, que llamaré imaginativamente 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
Esta versión hizo 36 millones de iteraciones. ¿Entonces supongo que Julia es más rápida en el looping? ¡Hurra! Bueno, en realidad el trabajo principal en este bucle son las llamadas a time()
... Julia es más rápida en generar muchas time()
llamadas.
¿Por qué es extraño cronometrar esto? Como dije, la mayor parte del trabajo real aquí está llamando time()
. El resto del ciclo realmente no hace nada. En un lenguaje compilado optimizado, si el compilador ve un ciclo que no hace nada, lo eliminará por completo. Por ejemplo:
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
Woah, cero segundos! ¿Cómo es eso posible? Bueno, echemos un vistazo al código LLVM (algo así como un código de máquina, pero para una máquina imaginaria que se usa como una representación intermedia), esto se reduce a:
julia> @code_llvm h()
; @ REPL[16]:1 within `h'
define i64 @julia_h_293() {
top:
; @ REPL[16]:6 within `h'
ret i64 5000000050000000
}
El compilador ve el ciclo, descubre que el resultado es el mismo cada vez y simplemente devuelve ese valor constante en lugar de ejecutar el ciclo. Lo cual, por supuesto, lleva cero tiempo.