¿Qué es más rápido, Clojure o ClojureScript(y por qué)?
v8 (3)
Si tuviera que adivinar, estoy bastante seguro de que la respuesta es Clojure, pero no estoy seguro de por qué. Lógicamente (para mí) parece que ClojureScript debería ser más rápido:
Ambos son "dinámicos", pero ClojureScript
- Compila a JavaScript, ejecutándose en V8
- El motor V8 es posiblemente el motor de lenguaje dinámico más rápido que existe
- V8 está escrito en C
mientras que Clojure:
- También es dinámico
- Se ejecuta en JVM, que no tiene soporte dinámico integrado, por lo que estoy pensando que JVM tiene que hacer lo que V8 está haciendo también, para habilitar el soporte dinámico
- y Java es más lento que C
Entonces, ¿cómo podría Clojure ser más rápido que ClojureScript? ¿"Dinámico" significa algo diferente cuando se dice que JavaScript es dinámico y Clojure es dinámico? ¿Qué no estoy viendo?
(Por supuesto, si ClojureScript es más rápido, ¿es correcto el razonamiento anterior?)
Supongo que, para lo que compila Clojure ... es al menos parte de la pregunta. Sé que la parte JVM no puede ser simplemente un intérprete simple (de lo contrario, ClojureScript sería más rápido), pero Clojure no puede compilar en el bytecode normal, ya que no hay "dinámica" en la JVM. Entonces, ¿cuál es la diferencia entre cómo se compila / ejecuta ClojureScript y cómo se compila / ejecuta Clojure y cómo se compila / ejecuta Java, y las diferencias de rendimiento implícitas en cada una?
En realidad, V8 está escrito en C ++. Sin embargo, hace básicamente lo mismo que la JVM, y la JVM está escrita en el código Javascript de C. V8 JIT y ejecuta el código JIT. Del mismo modo, el JVM JIT compila (o hotspot compila) el bytecode (NO Java) y ejecuta ese código generado.
Bytecode no es estático, como lo es Java. De hecho puede ser bastante dinámico. Java, por otro lado, es en su mayoría estático, y no es correcto combinar Java con el código de bytes. El compilador de java transforma el código fuente de Java en un bytecode, y la JVM ejecuta el bytecode. Para más información, te recomiendo que mires el blog de John Rose ( example ). Hay mucha información buena allí. Además, intente buscar charlas de Cliff Click (como esta ).
Del mismo modo, el código Clojure se compila directamente en el bytecode, y la JVM realiza el mismo proceso con ese bytecode. La compilación de Clojure se realiza generalmente en tiempo de ejecución, que no es el proceso más rápido. Asimismo, la traducción de Clojurescript a Javascript tampoco es rápida. La traducción de Javascript de V8 a una forma ejecutable es obviamente bastante rápida. Sin embargo, Clojure puede ser compilado por adelantado a bytecode, y eso puede eliminar una gran cantidad de sobrecarga de inicio.
Como dijo, tampoco es realmente correcto decir que la JVM interpreta el código de bytes. ¡La versión 1.0 hizo eso hace más de 17 años!
Tradicionalmente, había dos modos de compilación. El primer modo es un compilador JIT (Just in Time). Donde el bytecode se traduce directamente al código de máquina. La compilación JIT de Java se ejecuta rápidamente y no genera código altamente optimizado. Funciona bien.
El segundo modo se llama el compilador de punto de acceso. El compilador de hotspot es muy sofisticado. Inicia el programa muy rápidamente en modo interpretado, y lo analiza a medida que se ejecuta el programa. A medida que detecta puntos de acceso (puntos en el código que se ejecutan con frecuencia), los compilará. Mientras que el compilador JIT tiene que ser rápido porque nada se ejecuta a menos que se haya JIT, el compilador de punto de acceso puede permitirse gastar tiempo adicional para optimizar la moco del código que está compilando.
Además, puede volver atrás y revisar ese código más adelante y aplicarle aún más optimizaciones si es necesario y posible. Este es el punto donde el compilador del punto de acceso puede comenzar a batir C / C ++ compilado. Debido a que tiene conocimiento del código en tiempo de ejecución, puede permitirse aplicar optimizaciones que un compilador estático de C / C ++ no puede hacer. Por ejemplo, puede en línea funciones virtuales.
Hotspot tiene otra característica que, según mi conocimiento, no tiene otro entorno, también puede desoptimizar el código si es necesario. Por ejemplo, si el código tomaba continuamente una sola rama, y eso estaba optimizado y las condiciones de tiempo de ejecución cambian, obligando al código a bajar la otra rama (no optimizada) y el rendimiento de repente se vuelve terrible. Hotspot puede desoptimizar esa función y comenzar de nuevo el análisis para descubrir cómo hacerlo funcionar mejor.
Un inconveniente de hotspot es que comienza un poco lento. Un cambio en la Java 7 JVM ha sido combinar el compilador JIT y el compilador de punto de acceso. Sin embargo, este modo es nuevo y no es el predeterminado, pero una vez que se inicia, debe ser bueno y luego puede comenzar las optimizaciones avanzadas en las que JVM es tan bueno.
¡Aclamaciones!
Esto no es tanto una respuesta como un comentario histórico: tanto el HotSpot VM como el motor V8 js pueden tener sus orígenes en el proyecto Self en Sun Microsystems, que creo que prototipó gran parte de la tecnología que les permite correr tan rápido como ellos. Algo a tener en cuenta al compararlos a ambos. Hubiera publicado esto como un comentario, pero el sistema de reputación me lo impidió.
Esta pregunta es difícil de responder con precisión, sin hacer referencia a una tarea de referencia específica (o incluso a versiones específicas de Clojure o ClojureScript).
Dicho esto, en la mayoría de las situaciones, esperaría que Clojure fuera algo más rápido. Razones:
- Por lo general, Clojure se compila en código estático , por lo que en realidad no realiza ninguna búsqueda dinámica en tiempo de ejecución. Esto es muy importante: el código de alto rendimiento a menudo produce un código de bytes que es muy similar a Java de tipo estático. La pregunta parece estar haciendo la falsa suposición de que un lenguaje dinámico tiene que hacer búsquedas de métodos dinámicos en tiempo de ejecución: este no es siempre el caso (y generalmente no está en Clojure)
- El JVM JIT está muy bien diseñado, y creo que todavía es un poco mejor que el JavaScript JIT, a pesar de lo bueno que es el V8.
- Si necesita concurrencia o necesita aprovechar múltiples núcleos, claramente no hay competencia ya que JavaScript es de un solo hilo ...
- El compilador de Clojure es más maduro que ClojureScript, y ha tenido un montón de trabajo de ajuste de rendimiento en los últimos años (incluyendo cosas como soporte primitivo, protocolos, etc.)
Por supuesto, es posible escribir código rápido o lento en cualquier idioma. Esto hará más diferencia que la diferencia fundamental entre las implementaciones de lenguaje.
Y, lo que es más importante, su elección entre Clojure y ClojureScript no debería ser sobre el rendimiento en ningún caso. Ambos ofrecen ventajas de productividad convincentes. El principal factor decisivo debe ser:
- Si desea ejecutar en la web, utilice ClojureScript
- Si desea ejecutar en el servidor en un entorno JVM, utilice Clojure