page attribute javascript polymorphism monomorphism

attribute - ¿Por qué hacer materia monomórfica y polimórfica en JavaScript?



html page title (2)

He estado leyendo algunos artículos sobre la detección de cambios, y todos dicen que las funciones monomórficas son mucho más rápidas que las polimórficas. Por ejemplo, aquí hay una cita:

(...) El motivo es que debe escribirse de forma dinámica, para que pueda comprobar cada componente sin importar cómo se vea la estructura del modelo. A las máquinas virtuales no les gusta este tipo de código dinámico, porque no pueden optimizarlo. Se considera polimórfico ya que la forma de los objetos no es siempre la misma. Angular crea clases de detector de cambio en el tiempo de ejecución para cada componente, que son monomorfas, porque saben exactamente cuál es la forma del modelo del componente. Las máquinas virtuales pueden optimizar perfectamente este código, lo que lo hace muy rápido de ejecutar. Lo bueno es que no tenemos que preocuparnos demasiado, porque Angular lo hace automáticamente. (..)

Source

Ahora, estaba tratando de encontrar ejemplos de monomoprhic vs polimórficos, pero no pude encontrarlos en ninguna parte. ¿Podría alguien preocuparse por explicar la diferencia, y por qué es más rápido?


La respuesta está en el hecho de que las máquinas virtuales pueden realizar una detección heurística de "funciones importantes", es decir, el código que se ejecuta cientos o incluso miles de veces. Si el recuento de ejecución de una función supera un límite predeterminado, el optimizador de máquinas virtuales podría recoger ese bit de código e intentar compilar una versión optimizada basada en los argumentos pasados ​​a la función. En este caso, presume que su función siempre será llamada con el mismo tipo de argumentos (no necesariamente los mismos objetos).

El motivo de esto está bien documentado en este documento de directriz específico de v8 en el que se explica la optimización de un número entero frente a un número general. Di que tienes:

function add(a, b) { return a + b; }

... y siempre estás llamando a esta función con enteros, este método podría optimizarse compilando una función que hace la suma de enteros en la CPU, lo cual es rápido. Si después de la optimización le da un valor no entero, la VM desoptimiza la función y vuelve a la versión no optimizada, ya que no puede realizar la suma de enteros en los no enteros y la función devolvería resultados erróneos.

En los idiomas en los que especifica métodos monomórficos sobrecargados, puede solucionar este problema simplemente compilando varias versiones del mismo nombre de método con diferentes firmas de argumentos que luego se optimizan por su cuenta. Esto significa que usted llama a diferentes métodos optimizados porque el uso de argumentos de tipos diferentes requiere que use un método diferente sobrecargado, por lo que no hay duda de qué método está utilizando.

Podría pensar que podría mantener varias copias de las funciones optimizadas en la VM y verificar los tipos para determinar qué función compilada optimizada debe usar. En teoría, eso funcionaría, si la comprobación de tipos antes de la invocación del método fuera gratuita o muy barata. En la práctica, ese no suele ser el caso, y probablemente querrá equilibrar las cosas con el código del mundo real para determinar el mejor umbral de compensación.

Aquí hay una explicación más generalizada del compilador optimizador de v8 en particular (de Google I / O 2012):

https://youtu.be/UJPdhx5zTaw?t=26m26s

En resumen: las funciones que se invocan con los mismos tipos una y otra vez se optimizan en el compilador JIT, por lo tanto, más rápido.


Que yo sepa, monomorfismo es un término muy poco común. Personalmente, nunca lo he escuchado para codificar. Sin embargo, para averiguar qué es el monomorfismo, creo que podemos inferir qué significa al observar qué es el polimorfismo.

Polimorfismo: es la idea de que muchos (poli-) objetos diferentes pueden representarse por el mismo tipo en la máquina / tiempo de ejecución / intérprete. Por ejemplo, en C #, puede tener tantas clases como desee que implementen ICloneable y cualquiera de ellas podría usarse en un constructor de copia para una lista vinculada genérica (por ejemplo). Clase sin probar completa aquí como ejemplo si está interesado

Ok, entonces, ¿qué significa monomorfo?

Monomórfico para mí significa que el intérprete del objeto maneja el tipo EXACTO que está esperando, y no es posible ninguna herencia o modificación del tipo esperado. En este contexto, con el lenguaje javascript escrito con pato, la máquina virtual dice "este objeto javascript tiene estas propiedades exactas , que son de estos tipos exactos y se nombran exactamente como son". En C #, si quisiéramos ser monomórficos, las restricciones de tipo genérico serían imposibles porque T tendría que ser del mismo tipo todo el tiempo.

mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html proporciona una buena guía sobre por qué esto es importante para el rendimiento. Para mí esto se puede resumir a continuación.

Los motores de Javascript querrían evitar las búsquedas de tablas para las propiedades, y en su lugar hacer compensaciones de punteros de objetos. Con el monomorfismo, las compensaciones de objetos para objetos en una línea de código dada siempre serán las mismas y la VM tiene un tiempo fácil para averiguar cómo realizar búsquedas con la adición de punteros en lugar de búsquedas en tablas.

En realidad, los motores intentan manejar una pequeña cantidad de objetos diferentes que pasan a la misma función, pero la máquina virtual será más rápida si el objeto siempre se ve igual en la misma línea de código.

Ejemplo para claridad

El siguiente ejemplo es un javascript válido, pero el argumento o para la función f1 NO es monomórfico, porque la máquina virtual necesita manejar dos objetos con formas diferentes que se pasan.

function f1(o) { console.log(o.prop1) console.log(o.prop2) } // ... o1 = { prop1: ''prop1'', prop2: ''prop2'' } o2 = { prop1: ''prop1'', prop2: ''prop2'', prop3: ''prop3'' } f1(o1) f1(o2)

El punto de la cita del enlace que proporcionó es que AngularJS fuera de la caja proporciona un código que hace que todos sus argumentos de función javascript sean "monomorfos" porque los objetos que se pasan a ellos tienen la misma estructura cada vez que se llaman .