programming pinecone make language create performance programming-languages language-design interpreter

performance - pinecone - Lenguajes interpretados: ¿Cuanto más alto es el nivel más rápido?



make a programming language in java (9)

¿Cómo se hace una comparación justa?

Supongo que no está contando la velocidad de análisis, y supongo que está generando un conjunto de instrucciones de código de byte intermedio, y eso es lo que interpreta.

Supongamos que tiene un "punto de referencia" que es un bucle para sumar los elementos de una matriz. Si su lenguaje de nivel superior tiene instrucciones especiales de código de bytes para esto, de modo que en el lenguaje de nivel superior, haya menos códigos de bytes para interpretar, eso allí mismo explicaría por qué es más rápido.

He diseñado alrededor de 5 idiomas experimentales e intérpretes para ellos hasta el momento, para la educación, como pasatiempo y por diversión.

Una cosa que noté: el lenguaje de tipo ensamblador que presenta solo subrutinas y saltos condicionales como estructuras fue mucho más lento que el lenguaje de alto nivel que presenta if, while y así sucesivamente. Los desarrollé a la vez y ambos fueron interpretados en lenguajes. Escribí los intérpretes en C ++ e intenté optimizar la parte de ejecución de código para que fuera lo más rápido posible.

Mi hipótesis: en casi todos los casos, el rendimiento de los idiomas interpretados aumenta con su nivel (alto / bajo).

  • ¿Estoy básicamente en lo correcto con esto? (Si no, ¿por qué?)

EDITAR: No mencioné la palabra compilada aquí ni una sola vez, ¡se trata de interpretar contra interpretar!


Al final, analizar una línea de texto en su idioma tomará aproximadamente la misma cantidad de tiempo, ya sea que estemos hablando de ensamblaje en lugar de The Next Big Thing Language (TNBL). Esto no es cierto en un sentido literal, pero es cierto en una especie de Turing-esque, Big-O-Notation.

Si tarda (de nuevo, "aproximadamente") la misma cantidad de tiempo para determinar qué significa esto:

mov $3, $4, $5

como esto:

print "foo"

... entonces imaginemos escribir Hello World en nuestros dos idiomas. El intérprete de ensamblaje va a tener un programa mucho más complejo para analizar. Digamos, n líneas largas, que es n veces tantas líneas como el TNBL Hello Wolrld. Así que su sobrecarga más básica es n veces más larga.

Además de eso, tiene todo el código que ejecuta en un lenguaje más simple que modela el comportamiento de registros, operaciones, etc. Es mucho trabajo. En TNBL, hay casi un mapeo uno a uno entre la semántica del código interpretado y algo que puede hacer en el idioma principal. Esto significa una sobrecarga muy reducida al pasar de la semántica a la ejecución.

Estoy seguro de que verá a algunos programadores de Java que se opondrían a esta tesis, pero me gustaría señalar que Java tiene un par de ventajas: un bytecode intermedio que trata de obtener el código lo más cerca posible del hardware. antes de ejecutarlo, y miles de años-hombre se hundieron en el desarrollo de la optimización de ese lenguaje en tiempo de compilación y tiempo de ejecución. Apenas están en el continuo con un lenguaje aficionado. =]


Bueno, obviamente dependerá de cómo hayas implementado los diferentes idiomas.

Conjeturaría que es necesario interpretar más instrucciones para hacer lo mismo en un lenguaje interpretado de nivel inferior, y habrá una sobrecarga de tener que interpretar cada instrucción de bajo nivel en lugar de menos declaraciones de nivel superior.


En ambos casos estás interpetando código. Me imagino que en los idiomas de nivel superior tiene menos instrucciones para lograr la misma tarea, por lo que está dedicando menos tiempo a interpretar las instrucciones y más tiempo haciendo algo útil.


La realidad, por supuesto, es un poco más complicada que eso. A medida que los idiomas, los intérpretes y los compiladores se vuelven más sofisticados, surgen nuevas oportunidades para que la máquina optimice el rendimiento. Además, el rendimiento de cualquier programa dado depende en gran medida de la calidad del código que el programador haya escrito.

Además, el rendimiento afecta a diferentes tipos de programas de diferentes maneras. Las aplicaciones de línea de negocios, por ejemplo, casi siempre toman la mayor parte de su velocidad al buscar datos de una base de datos y enviarlos a través del cable, mientras que los programadores de juegos deben lidiar con un concepto de rendimiento completamente diferente, a saber, el marco de la tarjeta de video tarifas

Así que la imagen de rendimiento no es tan simple como podría parecer.


Si tuviera un lenguaje interpretado con un comando: runMyEntireProgramNatively() , sería más rápido que un lenguaje interpretado con más comandos. Cuanto menos haga cada comando, mayor será la proporción del tiempo dedicado a la interpretación para hacer cosas realmente.


Suponiendo que ambos se implementen de la misma manera, con el lenguaje de bajo nivel no optimizado y el lenguaje de alto nivel no expandido a un código intermedio de nivel inferior, "probablemente".

En tal caso, el lenguaje de alto nivel tendría la ventaja de abarrotar más código compilado (más rápido) por instrucción.

Sin embargo, en la práctica, un lenguaje interpretado de bajo nivel debería poder reducir la sobrecarga por instrucción, y suele ser mucho más sencillo implementar la compilación JIT. El grado de implementación de cada uno determinará finalmente cómo se comparan entre sí.

Diré que es más fácil implementar un lenguaje de alto nivel más rápido. (en mi limitada experiencia)


Yo diría que tienes la mitad de la razón. Si graficara la velocidad de interpretación, con el nivel de lenguaje en el eje X y la velocidad de ejecución en el eje Y, obtendría una curva de "bañera": interpretar un lenguaje de nivel extremadamente bajo puede ser bastante rápido, e interpretar una muy alta El lenguaje de nivel puede ser bastante rápido. Entre los dos, la interpretación es sustancialmente más lenta.

Dado un lenguaje de entrada de nivel extremadamente alto (p. Ej., APL), se obtiene una sobrecarga de interpretación mínima, ya que puede realizar una gran cantidad de trabajo en función del análisis del código de entrada relativamente pequeño.

En el extremo opuesto, puede obtener una velocidad bastante decente simplemente porque con un lenguaje de nivel suficientemente bajo, la interpretación se vuelve casi trivial. El intérprete interno de una implementación de Forth es un excelente ejemplo de esto. Estos a menudo representan un programa determinado de forma bastante compacta, que tiende a ser bastante fácil de almacenar en caché, por lo que al menos en teoría podría ejecutarse tan rápido como (o incluso algo más rápido que) el código de máquina puro.

La intención original era que la mayoría de las JVM, el entorno "administrado" de .NET, los intérpretes de código de bytes de Smalltalk, etc., encajaran en este último caso. Como la mayoría de los que han tratado de desarrollar estos han encontrado rápidamente, es difícil mantener los gastos generales de interpretación lo suficientemente bajos para lograr esto. Todos los que conozco que encajan en esta clase tienen el bucle interno escrito en lenguaje ensamblador a mano y generalmente por un buen programador de lenguaje ensamblador. Casi me atrevo a decir que si intenta utilizar un lenguaje de nivel superior (incluso C) va a ser más lento, y probablemente mucho más (cuando agregue una sobrecarga para cada instrucción de entrada, incluso una adicional) la instrucción en el bucle interno conducirá casi seguramente a una penalización de velocidad mensurable).


Resumen : el benchmarking es difícil. No se puede generalizar a partir de anécdotas.

Esta no es una regla apoyada por la evidencia.

¿Cómo está configurando su experimento del cual saca la conclusión "mayor = más rápido"? ¿Tiene implementaciones de ejemplo de algoritmos y conjuntos de pruebas idénticos que le permitan medir objetivamente un lenguaje interpretado en comparación con otro?

Si solo está creando pequeños mini-benchmarks, la probabilidad es muy alta de llegar a una conclusión a partir de muy pocos puntos de datos.

Por ejemplo, si un lenguaje interpretado tiene un operador de orden rápido, podría pensar "¡Ajá! ¡Más rápido!" que un lenguaje en el que tienes que implementar la clasificación a mano. Sin embargo, quicksort tiene el peor desempeño de O (n ^ 2). Si le doy a su operación de nivel superior un ejemplo de un conjunto de datos en el peor de los casos, una clasificación de fusión implementada manualmente diferente lo superará.