programming languages language and ruby performance interpreter interpreted-language

ruby - programming - compiled and interpreted languages



¿Por qué las llamadas al método Ruby son particularmente lentas(en comparación con otros idiomas)? (1)

Estoy tratando de leer sobre el rendimiento de Ruby, y encontré este hilo SO , donde una de las respuestas menciona que "las llamadas a métodos, una de las operaciones más comunes en Ruby, son particularmente lentas".

Otro hilo menciona que "hace una" búsqueda tardía "de métodos, para permitir flexibilidad. Esto lo ralentiza un poco. También tiene que recordar nombres por contexto para permitir eval, por lo que sus marcos y llamadas a métodos son más lentos".

¿Alguien puede explicar con más detalle por qué las llamadas al método Ruby son particularmente lentas y elaboradas en el segundo hilo? No estoy totalmente seguro de qué es la búsqueda tardía o por qué es lenta, y no sé qué significan los nombres por contexto o cómo se relaciona con los marcos y las llamadas a métodos.

Mi (posiblemente incorrecto) entendimiento es que dado que los métodos se pueden agregar o modificar en tiempo de ejecución, el intérprete de Ruby nunca puede "recordar" cómo ejecutar un método en particular, por lo que tiene que buscar el método cada vez que el programa se está ejecutando, y esto es lo que se entiende por llamadas de método que son lentas. Pero las correcciones y explicaciones más técnicas serían grandes.


Los lenguajes compilados a menudo tienen un rápido envío de métodos porque el código que llama conoce un índice en la clase ''vtable, que es una matriz de punteros a métodos. Después de solo unas pocas referencias de punteros, el código de llamada puede saltar directamente al método. El compilador crea el vtable y reemplaza cada nombre de método en el código fuente con el índice numérico del método en el vtable.

Los lenguajes dinámicos como Ruby a menudo tienen un despacho lento del método porque el código de llamada tiene un nombre para el método, no un puntero (ni un índice en una matriz que contiene los punteros). El código de llamada tiene que preguntar al objeto por su clase, luego tiene que preguntar a la clase si tiene un método con ese nombre, y si no, continuar por la cadena de ancestros preguntando a cada antepasado si tiene un método con ese nombre ( esto es lo que hace el compilador en un lenguaje compilado, razón por la cual la compilación es lenta y el envío del método es rápido). En lugar de unas pocas referencias de punteros que cuestan solo unas pocas instrucciones de máquina para invocar un método, un lenguaje dinámico debe ejecutar de docenas a cientos de instrucciones de máquina para buscar el método en la clase del objeto y en todas las clases ancestrales del objeto. Cada clase tiene una HashTable de nombres -> métodos, pero las HashTables con claves de cadena son un orden de magnitud más lento que las matrices con índices enteros.

Por supuesto, hay formas de optimizar el envío de métodos en idiomas dinámicos. En Ruby, es en lo que están trabajando JRuby, Rubinius y IronRuby. Pero ese es un tema para otra pregunta.