2012-04-12 26 views
10

Por lo tanto, estoy programando una simulación de física Javascript 2d. El rendimiento es bueno, pero estoy haciendo optimizaciones para mejorarlo. Entonces, como el programa funciona con mucha geometría física, hago varios cálculos del Teorema de Pitágoras en el programa. En total, alrededor de cinco cálculos; juntos, corren alrededor de un millón de veces por segundo. Entonces, pensé que aumentaría el rendimiento si puse ese simple código del teorema de Pitágoras en una nueva función y lo llamé; después de todo, de esa manera el navegador tiene menos compilación para hacer. Entonces, ejecuté el código en Firefox y obtuve .... 4000000% aumento en el tiempo de ejecución de ese cálculo.JS: ¿Cuánto tiempo se tarda en llamar a una función?

¿Cómo? Es el mismo código: Math.sqrt (x * x + y * y), entonces, ¿cómo lo hace más lento agregarlo como función? Supongo que la razón es que una función requiere tiempo para ser llamada, sin ejecutar el código, y ¿agregar que un millón de estas demoras por segundo lo ralentiza?

Eso me parece bastante alarmante. ¿Esto también se aplicaría a las funciones js predefinidas? Parece poco probable, y si es así, ¿cómo lo evitan?

El código utilizado para algo así:

function x() 
{ 
    dx=nx-mx; 
    dy=ny-my; 
    d=Math.sqrt(dx*dx+dy*dy); 
    doStuff(... 
} 

lo que intentaba era la siguiente:

function x() 
{ 
    dx=nx-mx; 
    dy=ny-my; 
    d=hypo(dx,dy); 
    doStuff(... 
} 
function hypo(x,y) 
{ 
    return Math.sqrt(x*x+y*y); 
} 

Gracias!

+3

¿Su función está definida fuera del alcance que se ejecuta un millón de veces por segundo? – alex

+0

Y no es cierto que el navegador tenga "menos compilación para hacer" porque lo pones en una función ... debería ser más o menos lo mismo, en realidad, sobre todo porque la compilación es una cuestión de inicio. Pero @alex probablemente obtuvo el motivo de tu 400% de ralentización :) – Ryan

+0

@alex Sí, está definido en la ventana principal. – mindoftea

Respuesta

7

Las llamadas de función son insignificantes o incluso optimizadas en los lenguajes precompilados que JS nunca ha tenido. Más allá de eso, mucho depende del navegador.

Son la muerte de todo el rendimiento en los idiomas interpretados que JS ha sido principalmente hasta hace relativamente poco tiempo. La mayoría de los navegadores modernos tienen compiladores JIT (Just In Time) que es una gran actualización de los intérpretes JS del pasado, pero creo que las llamadas a otras funciones aún cuestan un poco porque el objeto de llamada de JS tiene que determinar lo que realmente se llama y eso significa marchando arriba y abajo de varias cadenas de alcance.

Por lo tanto, como regla general: si te importa IE8 y las versiones anteriores y anteriores de Chrome y Firefox evita el período de llamadas de función. Especialmente dentro de los bucles. Para los navegadores JIT, esperaría que una función definida dentro de la otra función fuera en general beneficiosa (pero aún así probaría ya que esta es una nueva tecnología para IE9 y relativamente nueva para todos los demás).

Una cosa más a tener en cuenta. Si una función es particularmente compleja, los JIT pueden no hacer nada para optimizarlos.

https://groups.google.com/forum/#!msg/closure-compiler-discuss/4B4IcUJ4SUA/OqYWpSklTE4J

Pero lo más importante a entender es que cuando algo está bloqueado y sólo llama dentro de un contexto, como una función dentro de una función, que debe ser fácil para un JIT para optimizar. Definido fuera de una función, tiene que determinar qué definición de esa función se llama exactamente. Podría estar en una función externa. Podría ser global. Podría ser una propiedad del prototipo del constructor del objeto de la ventana, etc. En un lenguaje donde las funciones son de primera clase, lo que significa que sus referencias se pueden pasar como argumentos del mismo modo en que se pasan los datos, realmente no se puede evitar ese paso fuera de tu contexto actual.

Así que intente definir hypo dentro de X para ver qué pasa.

Otro par de consejos generales a partir de la edad interpretado que aún podría ser útil en equipos conjuntos de investigación: ''

  • El operador como en someObject.property, es un proceso que vale la memoria caché. Cuesta gastos generales ya que hay un proceso de búsqueda de objetos de llamada asociado cada vez que lo usa. Imagino que Chrome no preservaría los resultados de este proceso, ya que las alteraciones en los objetos o prototipos parentales podrían alterar lo que realmente hace referencia fuera de un contexto dado. En su ejemplo, si x está siendo utilizado por un bucle (probablemente correcto o incluso útil si x se define en la misma función que el bucle en JIT - asesinato en un intérprete), trataría de asignar Math.sqrt a una var antes de usarlo en hipo Tener demasiadas referencias a cosas fuera del contexto de su función actual puede hacer que algunos JIT decidan que no vale la pena optimizarlo, pero eso es pura especulación por mi parte.

  • El siguiente es probablemente la manera más rápida de bucle de una matriz:

//assume a giant array called someArray 
var i = someArray.length; //note the property lookup process being cached here 
//'someArray.reverse()' if original order isimportant 
while(i--){ 
    //now do stuff with someArray[i]; 
} 

nota: bloque de código que no trabaja aquí por alguna razón.

Hacerlo de esta manera puede ser útil porque básicamente transforma el paso inc/decremento y la comparación lógica en solo la disminución, eliminando por completo la necesidad de un operador de comparación izquierda/derecha. Tenga en cuenta que en JS, el operador de disminución del lado derecho significa que se pasa para ser evaluado y luego disminuir antes de que se use dentro del bloque. while(0) se evalúa como falso.

+0

Gracias; una muy buena explicación! Definir la función de hipo dentro de la función más grande requiere demasiado tiempo y la necesitaría en otras funciones, por lo que no vale la pena. Sin embargo, por curiosidad, traté de definirlo por dentro. ¡Eso resolvió el 97% de los problemas de rendimiento! Todavía está inflado, pero mucho mejor. ¿Sabes cómo las funciones predefinidas evitan esto? ¡Gracias de nuevo! – mindoftea

+0

Los objetos y métodos de Core JS están funcionando en un nivel mucho más bajo. Si intenta alertar a Math.sqrt, por ejemplo (sin parens) probablemente verá una función con [NATIVE CODE] o algo en el interior. En realidad, se trata de una llamada que sale del entorno de tiempo de ejecución precompilado del navegador. Sin embargo, aún puede ser útil guardar esas búsquedas en caché, ya que puede reemplazar esas referencias a los métodos definidos de forma nativa con lo que desee en JS (no es muy inteligente pero le permite hacer eso). Lo que significa que JIT aún debe tener en cuenta el proceso de búsqueda. –

+0

Además, si aún lo tienes todo configurado, prueba Hypo definido en línea con 'var sqrt = Math.sqrt' justo antes y luego llama a sqrt en lugar de hypo. Soy curioso. –

1

Para mi sorpresa, el almacenamiento en caché las operaciones de búsqueda según lo sugerido por Erik no hace mucho para mejorar el rendimiento en mi navegador (cromo, Linux), pero parece afecta negativamente al rendimiento en su lugar: http://jsperf.com/inline-metric-distance

var optimizedDistance = (function() { 
    var sqrt = Math.sqrt; 
    return function (x, y) { return sqrt(x * x + y * y); } 
})(); 

es más lenta que

var unoptimizedDistance = function(x, y) { 
    return Math.sqrt(x * x + y * y); 
} 

Incluso llamar a un alias es más lento

var _sqrt = Math.sqrt; // _sqrt is slower than Math.sqrt! 

Pero, de nuevo, esto no es una ciencia exacta y las medidas de la vida real todavía pueden variar.

Sin embargo, me gustaría ir con el uso de Math.sqrt.

+0

Interesante. Haré mis propias pruebas en un segundo. – mindoftea

+0

De acuerdo. He probado todo el código con y sin caché de Matemáticas. Los definí como globales para que mis diferentes funciones pudieran acceder a ellos y luego ejecutarlos. Parece que, salvo en algunos casos, el almacenamiento en caché fue de alguna manera más lento. – mindoftea

Cuestiones relacionadas