2010-03-28 20 views
11

actualización¿Por qué el programa Clojure Hello World es tan lento en comparación con Java y Python?

Como sugerido por muchas personas, parece que esto era debido al hecho de que el código fue compilado clojure primero y luego ejecutado. La compilación AOT debería ayudar a compensar eso. Dado que el proceso práctico de compilación Clojure AOT me resultó un tanto difícil de resolver (cuestiones de classpath, cuestiones de directorio, etc.), escribí un pequeño proceso paso a paso here, en caso de que alguien estuviera interesado.


Hola a todos,

estoy leyendo "Programación Clojure" y yo estaba comparando algunos idiomas que uso desde hace algún código simple. Noté que las implementaciones de clojure fueron las más lentas en cada caso. Por ejemplo,

Python - hello.py

def hello_world(name): 
    print "Hello, %s" % name 

hello_world("world") 

y el resultado,

$ time python hello.py 
Hello, world 

real 0m0.027s 
user 0m0.013s 
sys 0m0.014s 

Java - hello.java

import java.io.*; 

public class hello { 
    public static void hello_world(String name) { 
    System.out.println("Hello, " + name); 
    } 

    public static void main(String[] args) { 
    hello_world("world"); 
    } 
} 

y el resultado,

$ time java hello 
Hello, world 

real 0m0.324s 
user 0m0.296s 
sys 0m0.065s 

y, finalmente,

Clojure - hellofun.clj

(defn hello-world [username] 
    (println (format "Hello, %s" username))) 

(hello-world "world") 

y resultados,

$ time clj hellofun.clj 
Hello, world 

real 0m1.418s 
user 0m1.649s 
sys 0m0.154s 

eso es un conjunto, garangutan 1,4 segundos!

¿Alguien tiene indicaciones sobre la causa de esto? ¿Clojure realmente es tan lento o hay trucos de JVM y otros que se deben usar para acelerar la ejecución?

Más importante aún: ¿no será esta gran diferencia en el rendimiento un problema en algún momento? (Quiero decir, digamos que estaba usando Clojure para un sistema de producción; la ganancia que obtengo al utilizar lisp parece completamente contrarrestada por los problemas de rendimiento que puedo ver aquí).


La máquina que se utiliza aquí es un MacBook Pro con Snow Leopard 2007, un 2,16 GHz Intel C2D y 2G DDR2 SDRAM.

Por cierto, el guión CLJ que estoy usando es de here y se ve como,

#!/bin/bash 
JAVA=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java 
CLJ_DIR=/opt/jars 
CLOJURE=$CLJ_DIR/clojure.jar 
CONTRIB=$CLJ_DIR/clojure-contrib.jar 
JLINE=$CLJ_DIR/jline-0.9.94.jar 
CP=$PWD:$CLOJURE:$JLINE:$CONTRIB 

# Add extra jars as specified by `.clojure` file 
if [ -f .clojure ] 
then 
    CP=$CP:`cat .clojure` 
fi 

if [ -z "$1" ]; then 
    $JAVA -server -cp $CP \ 
     jline.ConsoleRunner clojure.lang.Repl 
else 
    scriptname=$1 
    $JAVA -server -cp $CP clojure.main $scriptname -- $* 
fi 
+2

Cada vez que ejecuta el ejemplo Clojure, se compila. Si desea medir el rendimiento de ejecución, AOT compila. – fogus

+1

Existe una muy buena razón por la que no todos son lo suficientemente valientes como para comparar el código. No es fácil. No puedes simplemente ejecutar RUN y mirar un cronómetro. – Rayne

+1

@fogus - gracias, estoy buscando compilación AOT. @Rayne - No estoy "evaluando" el código en un sentido literal. Quería una explicación de por qué funcionaba lento en comparación con otros lenguajes, y la explicación de compilación a pedido frente a AOT me ayudó a entender eso. – viksit

Respuesta

27

No está midiendo mucho aquí excepto en el momento del arranque Clojure. También está ejecutando su programa de tal forma que también está midiendo el tiempo de compilación. Si desea ver tiempos de carga más rápidos, necesitará compilar su código antes de tiempo.

Al haber codificado un poco en Python, he descubierto que Clojure es, por lo general, mucho, mucho, mucho más rápido que Python y generalmente se puede obtener un programa Clojure para obtener entre 2X-4X de la velocidad de Java puro .

+0

¿Qué opinas sobre clojure en comparación con PyPy? Recientemente me sorprendió que algunos cálculos de teoría de grafos que hice fueran significativamente más rápidos en PyPy que en clojure (y clojure fue significativamente más rápido que CPython). El código en ambos casos es principalmente funcional, usa matrices (listas en Python, vectores en clojure), conjuntos y secuencias perezosas (generadores en Python). –

+2

Si está comparando listas mutables con vectores, verá una diferencia de rendimiento. En Clojure, cuando la perforación realmente importa, usaría matrices de Java o una ligera abstracción sobre ellas. Dicho esto, el rendimiento de PyPy parece realmente bueno. Sin embargo, ahora que estoy familiarizado/cómodo con salir perfumado de Clojure, creo que hay algunas maneras de hacerlo antes de que PyPy compita con JVM. – dnolen

2

Para añadir a la dnolen respuesta, cuando haciendo Python vs contenidos de sincronización Clojure, es posible que desee empaquetar su "unidad básica de trabajo" como una función y luego usar el time macro (en Clojure) o la función timeit.timeit (en Python o, mejor aún, utilice las funciones de sincronización de IPython) en REPL. Los resultados deberían ser aproximadamente comparables. (Tenga en cuenta que el código de Clojure debe "calentarse" ejecutándolo varias veces para lograr un rendimiento completo.)

También hay algunas suites de referencia para Clojure, por ejemplo Criterium; es posible que desee considerar el uso de uno de ellos.

+0

Gracias por la información. Ya uso python timeit para py benchmarking y perf analysis. Esta pregunta fue más sobre las líneas de - tal vez había un problema con la configuración de JVM, etc. que lo estaba causando. El hecho de que se compile AOT lo explica en gran medida. – viksit

+0

@viksit: Sí, el arranque de JVM es otro factor importante. (Obviamente ya no es un factor importante si su proceso tiene una cantidad significativa de trabajo que hacer, pero ese no es el caso con el ejemplo "Hola mundo", o con una sola pasada a través de la gran mayoría de microbenchmarks que yo ' visto.) –

+0

@downvoter: Por curiosidad, ¿con qué parte de esta respuesta no está de acuerdo? (Si es algo así como "el tiempo de Clojure no es exactamente como el tiempo de Python", anote la parte "aproximadamente comparable" de la respuesta (más el comentario sobre el calentamiento) y pregúntese si esto podría ser suficiente para are-we- en el mismo tipo de comparaciones de béisbol ... Si es otra cosa, sinceramente me gustaría saberlo) –

2

Además, tenga en cuenta que la opción '-server' en su script clj utilizará el 'servidor JVM' que está optimizado para procesos de larga ejecución a costa de un tiempo de arranque más lento.

Su ejemplo de Java no incluía esta opción, por lo que probablemente esté utilizando la "JVM del cliente", que está optimizada para un tiempo de inicio más rápido.

Prueba a ejecutar java -jar clojure.jar -i hellofun.clj para una comparación más justa.

1

La JVM en general ya tiene un tiempo de inicio algo lento en comparación con los lenguajes nativos o interpretados. Además de eso, Clojure agrega una sobrecarga considerable al tiempo de inicio, ya que compila y carga bastante código al inicio. Incluso con AOT, hay muchas cosas que Clojure necesita configurar antes de que pueda ejecutarse.

En pocas palabras, no dependa de Clojure para procesos breves. Ni siquiera confíe en Java para esos casos de uso la mayor parte del tiempo. Algo nativo o interpretado como Node.js, Python, Lua, etc. sería mucho mejor.

Para procesos de duración media a larga duración, Clojure será en promedio mucho más rápido que casi todos los demás lenguajes dinámicos, superando a Python y Ruby. Clojure se puede hacer casi tan rápido como Java si es necesario sin mucho esfuerzo, y su interoperabilidad de Java es tan fácil, que al cambiar algunas funciones a java puro, en la mayoría de los casos puede obtener una velocidad igual a Java.

Ahora, si realmente quisiera algo rápido para Clojure, le recomiendo que busque en lumo. Es un ClojureScript REPL que es autónomo y se ejecuta en ClojureScript bootstrapped, por lo que no se ve JVM.

time python -c "print(\"Hello World\")" 
Hello World 

real 0m0.266s 
user 0m0.015s 
sys  0m0.202s 

time lumo -e "\"Hello World\"" 
"Hello World" 

real 0m0.438s 
user 0m0.000s 
sys  0m0.203s 

Como puede ver, Lumo se acerca bastante a la velocidad de inicio de Cpy3k.

Una alternativa, que ya no será Clojure, pero seguirá siendo un Lisp inspirado en Clojure, es Hy. Es un Lisp con la sintaxis de Clojure ejecutándose en Python.

time hy -c "(print \"Hello World\")" 
Hello World 

real 0m0.902s 
user 0m0.000s 
sys  0m0.171s 

Su tiempo de inicio es ligeramente más lento que tanto Cpy3k y Lumo, pero te da todos Python a su disposición con la sintaxis y macros de Clojure.

+0

¡Gracias por eso! He estado siguiendo a Lumo ahora por un tiempo, y le he dado una consideración seria para un proyecto. ¿Has usado hy para cualquier nivel de producción? Editar: También, una nueva respuesta después de 6 años. Maldita sea, el tiempo vuela. – viksit

+0

@viksit No he usado Hy en prod, solo jugué con él en mi propio tiempo. –

Cuestiones relacionadas