Rendimiento de Julia (Julia-lang) Comparado con Fortran y Python


Adapté un programa simple para calcular y trazar los vórtices de movimiento de Julia para probar el lenguaje, también lo escribí en Python sin ninguna razón en particular.

(Descargo de responsabilidad: 1. Cada comparación de rendimiento en stackoverflow que leo se cierra por no ser completa / correcta/bien escrita / relevante, etc. sucesivamente. - No estoy fingiendo que esto es una comparación real, sólo me gustaría saber cómo hacer que Julia sea más rápida. 2. Sé que Python podría ser optimizado, implementado en Cython etc, eso no es parte de esta discusión, solo está aquí para una referencia de funciones equivalentes en Julia y Python.)

El código y los resultados de rendimiento se pueden ver en un gist.

El rendimiento de Julia es significativamente más lento que el de Fortran. Los tiempos necesarios para realizar el cálculo en sí son (50000 pasos de tiempo):

Fortran: 0.051s
Julia: 2.256s
Python: 30.846s

Julia es mucho más lenta (~44 veces lenta) que Fortran, la brecha se estrecha pero sigue siendo significativa con 10 veces más pasos de tiempo (0.50s vs 15.24s ).

Estos resultados son significativamente diferentes a los que se muestran en la página principal de julia. ¿Qué estoy haciendo mal? ¿Puedo arreglar la Julia para que sea significativamente más rápida?

He leído la página Consejos de rendimiento de Julia y el código detrás de la comparación en la página de inicio de Julia y nada se destaca para mí para arreglar.

También es interesante que Julia sea extremadamente lenta para cargar PyPlot (5secs ish!!) y mucho más lento que Python para leer el archivo de texto. Podría hacer ¿algo para mejorar estas cosas?

Tenga en cuenta que los tiempos anteriores no muestran el tiempo de carga para Julia y Python, es solo el tiempo bruto tomado para el cálculo AFAIK - ver el código. Para Fortran es todo. El trazado se ha desactivado, aproximadamente, en cada caso para permitir la comparación de velocidad.

Computadora: Intel i7-3770, 16 GB de ram, SSD HD, sistema operativo: Ubuntu 13.10 64bit., Fortran: gfortran, GNU Fortran (Ubuntu / Linaro 4.8.1-10ubuntu9) 4.8.1, Julia: Versión 0.3.0-versión preliminar + 396 (2013-12-12 00:18 UTC), Commit c5364db* (0 days old master), x86_64-linux-gnu, Python: 2.7.5 +


Actualización:

Basado en el consejo de ivarne reescribí el script de Julia (actualizado en el resumen anterior): encapsulando el trabajo grunt en funciones, declarando el tipo de todo y dividiendo diferentes elementos de matrices en diferentes arrays donde sea aplicable. (Incluí Float64 en bastantes lugares, ya que probé Float32 para ver si eso ayudó, no lo hizo la mayor parte del tiempo).

El los resultados son los siguientes:

50,000 pasos de tiempo:

Fortran: 0.051s (entire programme)
Julia: raw calc.: 0.201s, calc. and return (?): 0.758s, total exec.: 6.947s

500,000 pasos de tiempo:

Fortran: 0.495s (entire programme)
Julia: raw calc.: 1.547s, calc. and return (?): 2.094s, total exec.: 8.521s

En conclusión:

  • Puedes acelerar a Julia un poco.

  • Puede afectar significativamente la aparentemente velocidad de Julia dependiendo de cómo mida su rendimiento.

Author: Wilfred Hughes, 2013-12-16

2 answers

He seguido el proyecto Julia desde hace un tiempo, y tengo algunos comentarios al código que podrían ser relevantes.

  • Parece que ejecuta una cantidad sustancial del código en el ámbito global. El entorno global es actualmente muy lento en Julia, porque los tipos todas las variables tienen que comprobarse en cada iteración. Los bucles generalmente deben escribirse en una función.
  • Parece que usas corte de matriz. Actualmente eso hace una copia porque Julia no tiene vistas de matriz rápida. Puede intentar cambiarlos por subarray, pero actualmente son mucho más lentos de lo que deberían.

El tiempo de carga de PyPlot (y de cualquier otro paquete) es un problema conocido, y se debe a que analizar y compilar el código de Julia a código máquina consume mucho tiempo. Hay ideas sobre tener una caché para este proceso para que este proceso se vuelva instantáneo, pero aún no está terminado. La biblioteca Base se almacena actualmente en caché en estado compilado, por lo que la mayor parte de la infraestructura está en el maestro rama ahora.

AÑADIDO : Intenté ejecutar la prueba en una función aislada y obtuve estos resultados. Ver esto esencia

Análisis:

elapsed time: 0.334042578 seconds (11797548 bytes allocated)

Y runas consecutivas de árbol del bucle de prueba principal.

elapsed time: 0.62999287 seconds (195210884 bytes allocated)
elapsed time: 0.39398753 seconds (184735016 bytes allocated)
elapsed time: 0.392036875 seconds (184735016 bytes allocated)

Observe cómo el tiempo mejoró después de la primera ejecución, porque el código compilado se usó de nuevo.

Actualización 2 Con un poco de manejo de memoria mejorado (asegurar la reutilización de matrices, porque la asignación no se copia), tengo el tiempo hasta 0.2 segundos (en mi máquina). Definitivamente hay más que se podría hacer para evitar la asignación de nuevos arrays, pero luego comienza a ser un poco complicado.

Esta línea no hace lo que piensas:

vx_old = vx

Pero esto haz lo que quieras:

copy!(vx_old, vx)

Y devectorizar un bucle.

x += 0.5*(vx + vx_old)*delta_t
y += 0.5*(vy + vy_old)*delta_t

A:

for i = 1:nvortex
    x[i] += 0.5*(vx[i] + vx_old[i])*delta_t
    y[i] += 0.5*(vy[i] + vy_old[i])*delta_t
end
 20
Author: ivarne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-07 09:33:57

@ivarne cubre esto, pero merece un poco más de atención:

julia> @time x=[1:10000];
elapsed time: 1.544e-5 seconds (80120 bytes allocated)

julia> @time y = x[1:10000];
elapsed time: 2.6857e-5 seconds (80120 bytes allocated)

Wow. Eso es mucho tiempo y memoria.

julia> @time z = sub(x,1:10000);
elapsed time: 6.239e-6 seconds (296 bytes allocated)

Mucho mejor. ¿Por qué [:] no hace lo que sub hace? No sé. Bueno, en cierto modo sí. Cuando vas a index z[10] Julia piensa, hrmm, z es como x excepto que los índices están compensados por 0 así que z[10] es x[10+0]. Ahí lo tienes. Esa pequeña adición adicional le costará a largo plazo si hace una gran cantidad de indexación. Para solucionar esto se necesita un concepto como punteros que es contra la religión de Julia.

Actualización Julia ahora desaprueba [:] (versión 0.4.0)

julia> @time x=[1:10000];
WARNING: [a] concatenation is deprecated; use collect(a) instead
in depwarn at deprecated.jl:73
in oldstyle_vcat_warning at ./abstractarray.jl:29
in vect at abstractarray.jl:32
while loading no file, in expression starting on line 155
0.530051 seconds (180.12 k allocations: 9.429 MB, 5.26% gc time)

julia> @time x=[1:10000];
WARNING: [a] concatenation is deprecated; use collect(a) instead
in depwarn at deprecated.jl:73
in oldstyle_vcat_warning at ./abstractarray.jl:29
in vect at abstractarray.jl:32
while loading no file, in expression starting on line 155
0.001373 seconds (303 allocations: 714.656 KB)

Recoger es más rápido

julia> @ time x=collect(1:10000);
0.003991 seconds (35 allocations: 80.078 KB)

julia> @ time x=collect(1:10000);
0.000031 seconds (8 allocations: 78.406 KB)

Comparable a SubArrays

julia> @time z = sub(x,1:10000);
0.067002 seconds (36.27 k allocations: 1.792 MB)

julia> @time z = sub(x,1:10000);
0.000016 seconds (7 allocations: 288 bytes)
 3
Author: Michael Fox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-08 19:36:15