2010-11-10 4 views
11

Ver http://shootout.alioth.debian.org/u32q/compare.php?lang=clojure Clojure es mucho más lento que java -server, mientras que scala no lo es.¿Por qué clojure funciona peor que scala en los puntos de referencia alioth?

¿Qué ofrece?

ref: On Performance and Java Interoperability: Clojure vs. Scala

+0

Se agregó una etiqueta de "rendimiento", ya que es donde se encuentran la mayoría de las preguntas como esta. –

+0

Clojure aún no está cocinado - Clojure 1.0 fue lanzado en mayo de 2009, el primer lanzamiento público de Scala fue en 2003. – igouy

Respuesta

11

Puede escribir código rápido o lento en cualquier idioma :-)

Sobre la base de una inspección rápida de una parte del código Clojure, diría que la principal razón para la diferencia de rendimiento es que el código de referencia Clojure aún no se ha optimizado completamente para usar las funciones de idioma más rápidas disponibles.

Por ejemplo las siguientes características en Clojure son todos muy fresco y útil para la conveniencia del desarrollo, pero incurren en alguna sobrecarga de rendimiento en tiempo de ejecución:

  • secuencias perezosos y listas
  • dinámico de Java interoperabilidad mediante reflexión
  • tiempo de ejecución función composición/funciones de primera clase
  • Multimétodos/envío dinámico
  • compilación dinámica con eval o en el REPL
  • BigInteger aritmética

Si quieres absoluta de rendimiento máximo (a costa de cierta complejidad adicional), desea volver a escribir código para evitar estos y usar cosas como:

  • insinuación de tipo estático (para evitar la reflexión)
  • Transitorios
  • macros (para la manipulación de código de tiempo de compilación)
  • Protocolos
  • primitivos de Java y matrices
  • bucle/repiten para la iteración

Con el uso juicioso de lo anterior, he encontrado que por lo general es posible llegar muy cerca de rendimiento de Java en Clojure 1.2+, por ejemplo, considere el siguiente código para hacer un millón de adiciones:

Desobligado Clojure usando una secuencia perezosa y una aritmética de biginterger. Es agradable y funcional pero no es especialmente rápido:

(reduce 
    (fn [acc val] (unchecked-int (unchecked-add (int acc) (int val)))) 
    (range 0 1000000)) 

=> "Elapsed time: 65.201243 msecs" 

optimizado Clojure con la aritmética primitiva y el lazo/Repetir:

(loop [acc (int 0) i (int 0)] 
    (if (>= i (int 1000000)) 
    acc 
    (recur (unchecked-add acc i) (unchecked-inc i)))) 

=> "Elapsed time: 0.691474 msecs" 

Java código, un bucle iterativo bastante estándar:

public static int addMillion() { 
    int result=0; 
    for (int i=0; i<1000000; i++) { 
     result+=i; 
    } 
    return result; 
} 

=> "Elapsed time: 0.692081 msecs" 

ps He usado unchecked-add en lugar de + en el código Clojure para que coincida con el comportamiento de desbordamiento de enteros de Java.

+0

Usted dice que ' Hemos analizado algunos de los códigos y sugerido funciones que "incurren en una sobrecarga de rendimiento en el tiempo de ejecución", con la implicación de que esas características tienen algo que ver con la sobrecarga de rendimiento de los programas que miraste, pero no dices qué características piensas efecto qué programas. Tal vez ninguno de los programas medidos usa secuencias perezosas o multimétodos o ... – igouy

+10

Puede demostrar que es específicamente "posible acercarse mucho al rendimiento de Java en Clojure 1.2+" contribuyendo con programas que sí ;-) – igouy

+0

Tengo una implementación de lista con acceso aleatorio rápido escrito en Clojure (orientación 1.2) que es exactamente el la misma velocidad para el acceso aleatorio como un vector que está escrito en Java. Por lo tanto, es posible bajar a la velocidad de Java. Sin embargo, no es un punto de referencia cuestionable, sino algo útil en los programas reales. Entonces eso quizás no cuenta como demostración. – kotarak

4

Clojure es un lenguaje dinámico, que tiene que hacer un montón de tiempo de ejecución de comprobación adicional.

Editar, ahora en un estado de ánimo menos frívolo: Sin mirar las implementaciones reales Clojure de los puntos de referencia, la diferencia de rendimiento también puede atribuirse al hecho de que el código Clojure idiomático evita el estado mutable y, en igualdad de condiciones , los algoritmos que usan estado mutable a menudo superan a los que no lo hacen.

Ya sea un programador dado es capaz de escribir código que utiliza el estado mutable correctamente es una cuestión diferente, y por supuesto la naturaleza declarativa de código puramente funcional significa que una gran cantidad de optimizaciones se hacen posibles, al menos en teoría.

+0

Esto es simplemente incorrecto. – dnolen

+1

@dnolen: ¿Estás diciendo que clojure no realiza comprobaciones de tipo en el tiempo de ejecución? O eso Scala hace? – sepp2k

+3

@dnolen Esto simplemente está mal porque _______. La respuesta correcta es ________. Si sabes algo que nosotros no sabemos, ¡por favor explica! –

-1

Las funciones de Scala se introdujeron generalmente poco a poco, y en la mayoría de los casos especificando el código de Java correspondiente. Esto convierte a Scala en una evolución de Java que tiene en cuenta el equilibrio entre las nuevas funciones y el rendimiento.

Clojure, por otro lado, fue diseñado con un enfoque muy diferente a la programación en mente, y tiene características para interoperar con Java. Sin embargo, la falta de coincidencia de impedancia entre ambos lenguajes (y la JVM está preparada para Java), puede hacer que Clojure sea un poco más lento. Para algunos problemas, Clojure puede ser más rápido (por ejemplo, utilizando colecciones inmutables), pero tal vez no para la mayoría de las aplicaciones.

+0

En la mayoría de los casos, las estructuras de datos persistentes de Clojure deberían ser un poco más lentas que las estructuras de datos de Java correspondientes. Sin embargo, serán correctos a la vista del acceso concurrente. :) –

+1

Clojure * está * explícitamente diseñado para lograr el rendimiento a la par con Java. Pero todavía es un trabajo en progreso llegar allí. –

+0

@Alex: Aha, sí, al igual que Lisp fue diseñado para lograr el rendimiento del lenguaje ensamblador ... Sólo después de 25 años todavía está trabajando en progreso :) –

1

Muchos de los puntos de referencia son más lentos porque hasta hace muy poco tiempo se necesitaba mucho más trabajo y conocimiento experto para obtener un buen rendimiento de Clojure. Estos puntos de referencia apuntan principalmente a 1.2.

Clojure 1.3 tiene bastantes mejoras que hacen que escribir código de rendimiento sea mucho más simple. Aun así, no todas las personas de la comunidad están familiarizadas con la mejora del rendimiento del código de Clojure, por lo que será un proceso gradual a medida que las personas presenten mejores versiones.

+0

Clojure 1.3 aún no está cocido, por lo tanto - Clojure 1.3 Alpha 1 y Clojure 1.3 Alpha 2 y Clojure 1.3 Alpha 3 y ... – igouy

+0

supongo que estamos en al menos 1.7.0 en estos días ... – rogerdpack

3

Parte de esto se debe a la prueba en sí, es decir, que debe escribir el código (sea cual sea el idioma) para realizar los mismos pasos de implementación que la versión de Java. ¿Tiene una forma diferente, posiblemente más rápida de hacer X en su idioma? Demasiado.

Cuando la base para la prueba es para bucles y ataques de matriz y un estilo imperativo general que requiere múltiples bloques flotantes de código, obtendrá números sin sentido.

+1

AFAIK también hay una sección de programas "alternativa interesante". Aún se muestran programas que resuelven el problema, pero que no respetan las restricciones del índice de referencia. Hay muchos programas alternativos para, por ejemplo, C++ y python3 que son más rápidos que los presentados para el benchmark. Tal vez alguien pueda enviar un código clojure elegante incluso si solo es para la sección "programas alternativos". – Kintaro

+3

NO ES CIERTO. No tiene que escribir su código para "hacer exactamente los mismos pasos de implementación que la versión de Java". Tienes que usar el mismo algoritmo, por lo que para los dígitos pi tienes que usar ese "Algoritmo de espina ilimitada" de Haskell. – igouy

+3

No es cierto. Si su idioma tiene un comando interno más rápido que hace parte del trabajo que no es una prueba _explicit_ de cómo maneja su lenguaje la implementación de algún algoritmo, puede usarlo y limpiarlo. Sea testigo del rendimiento de JavaScript V8 regex, gracias al considerable esfuerzo de la gente de Google en la creación de un asombroso motor regex (utilizado por V8). Por supuesto, si no le dices a la gente que al menos use el mismo algoritmo, realmente solo estás probando quién puede proponer el algoritmo más inteligente, que es una pregunta diferente de cómo se desempeñan los diferentes idiomas cuando se les pide que realicen una tarea en particular. . –

2

Escribir algunas versiones más rápidas parece que podría ser divertido ...

Cuestiones relacionadas