2010-01-23 14 views
5

Tengo curiosidad acerca de cuál es la mejor práctica al hacer referencia al espacio de nombres 'global' en javascript, que es simplemente un acceso directo al objeto window (o viceversa dependiendo de cómo lo mires).En javascript, ¿acceder a 'window.Math' es más lento o más rápido que acceder al objeto 'Math' sin la 'ventana'?

Quiero saber si:

var answer = Math.floor(value); 

es mejor o peor que:

var answer = window.Math.floor(value); 

Es uno mejor o peor, aunque sea ligeramente, para obtener un rendimiento, uso de recursos, o la compatibilidad?

¿Tiene uno un costo más bajo? (Algo así como un puntero adicional o algo así)

editar nota: Aunque soy un nazi legibilidad sobre el rendimiento en la mayoría de situaciones, en este caso estoy haciendo caso omiso de las diferencias en la legibilidad de centrarse exclusivamente en el rendimiento.

+0

¿Por qué es importante? ¿Cuál es el problema que estás tratando de resolver? –

+0

Bueno, estoy optimizando para el rendimiento, así que estoy curioso de cuál es más rápido. Si hago miles de cálculos, una desreferencia de puntero menos por ciclo, probablemente me ayude. –

+2

"No es una pregunta real"? Esta es una buena pregunta. –

Respuesta

14

Antes que nada, nunca compare cosas como estas por motivos de rendimiento. Math.round es obviamente más fácil para los ojos que window.Math.round, y no verá un aumento notable en el rendimiento al usar uno u otro. Así que no ofusque su código para incrementos muy pequeños en el rendimiento.

Sin embargo, si solo tiene curiosidad acerca de cuál es más rápido ... No estoy seguro de cómo se busca el alcance global "bajo el capó", pero supongo que el acceso a window es el mismo que accediendo a Math (window y Math vivo en el mismo nivel, como lo demuestra el funcionamiento de window.window.window.Math.round). Por lo tanto, acceder a window.Math sería más lento.

Además, las variables manera se buscan, se podría ver un aumento de rendimiento haciendo var round = Math.round; y llamando round(1.23), ya que todos los nombres son vistos por primera vez en el ámbito local actual, entonces el alcance por encima de la actual, y así sucesivamente , todo el camino hasta el alcance global. Cada nivel de alcance agrega una muy ligera sobrecarga.

Pero nuevamente, no haga estas optimizaciones a menos que esté seguro de que harán una diferencia notable. El código comprensible y legible es importante para que funcione de la manera que debería, ahora y en el futuro.

Aquí hay un perfil completo usando Firebug:

<!DOCTYPE html> 
<html> 
    <head> 
     <title>Benchmark scope lookup</title> 
    </head> 
    <body> 
     <script> 
     function bench_window_Math_round() { 
      for (var i = 0; i < 100000; i++) { 
       window.Math.round(1.23); 
      } 
     } 

     function bench_Math_round() { 
      for (var i = 0; i < 100000; i++) { 
       Math.round(1.23); 
      } 
     } 

     function bench_round() { 
      for (var i = 0, round = Math.round; i < 100000; i++) { 
       round(1.23); 
      } 
     } 

     console.log('Profiling will begin in 3 seconds...'); 
     setTimeout(function() { 
      console.profile(); 
      for (var i = 0; i < 10; i++) { 
       bench_window_Math_round(); 
       bench_Math_round(); 
       bench_round(); 
      } 
      console.profileEnd(); 
     }, 3000); 
     </script> 
    </body> 
</html> 

Mis resultados:
Time muestra total de 100.000 llamadas * 10, Avg/Min/Max tiempo de la demostración de 100.000 llamadas.

Calls Percent Own Time Time  Avg  Min  Max 
bench_window_Math_round 
10  86.36% 1114.73ms 1114.73ms 111.473ms 110.827ms 114.018ms 
bench_Math_round 
10  8.21% 106.04ms 106.04ms 10.604ms 10.252ms 13.446ms 
bench_round 
10  5.43%  70.08ms 70.08ms 7.008ms 6.884ms 7.092ms 

Como se puede ver, window.Math es una muy mala idea. Supongo que acceder al objeto global window agrega una sobrecarga adicional.Sin embargo, la diferencia entre acceder al objeto Math desde el alcance global y acceder a una variable local con referencia a la función Math.round no es muy buena ... Tenga en cuenta que esto es 100,000 llamadas, y la diferencia es solo 3.6 Sra. Incluso con un millón de llamadas, solo verías una diferencia de 36 ms.

cosas en que pensar con el código anterior: perfiles

  • Las funciones son en realidad levantaron la vista de otro ámbito, lo que implica una sobrecarga (apenas perceptible, sin embargo, he intentado importar las funciones en la función anónima).
  • La función real Math.round agrega sobrecarga (supongo que unos 6ms en 100,000 llamadas).
+0

+1 por dar mi uso de var round = Math.round alguna validez. –

+0

+1 ¡Gran respuesta, Blixt! – roosteronacid

+0

buen trabajo, hiciste un gran trabajo con los puntos de referencia. Desearía poder darte un +2 –

2

El rendimiento de JS difiere ampliamente de un navegador a otro.

Mi consejo: punto de referencia. Solo póngalo en un ciclo for, déjelo correr unos millones de veces, y cronometralo ... vea lo que obtiene. ¡Asegúrate de compartir tus resultados!

0

Si se llama a Math.round() en un alcance local/de función, el intérprete tendrá que verificar primero una var local y luego en el espacio global/ventana. Entonces, en el ámbito local, mi conjetura sería que window.Math.round() sería muy ligeramente más rápido. Esto no es ensamblado, o C o C++, así que no me preocuparía por cuál es más rápido para las razones de rendimiento, pero si por curiosidad, seguro, compárelo.

+1

'window.Math.round()' todavía tendría que buscar una variable 'window' local. –

1

(Como usted ha dicho) Math.floor probablemente va a ser un atajo para window.Math (como window es un objeto global Javascript) en la mayoría de las implementaciones de Javascript como V8.

Spidermonkey y V8 estarán tan optimizados para el uso común que no debería ser una preocupación.

Para la legibilidad mi preferencia sería usar Math.floor, la diferencia de velocidad será tan insignificante que no vale la pena preocuparse por ella. Si está haciendo 100.000 pisos, probablemente sea hora de cambiar esa lógica del cliente.

Es posible que desee echarle un vistazo a la fuente de v8. Hay algunos comentarios interesantes sobre el afeitado de nanosegundos de funciones como thisint.Parse().

// Some people use parseInt instead of Math.floor. This 
// optimization makes parseInt on a Smi 12 times faster (60ns 
// vs 800ns). The following optimization makes parseInt on a 
// non-Smi number 9 times faster (230ns vs 2070ns). Together 
// they make parseInt on a string 1.4% slower (274ns vs 270ns). 
+0

@Mark Yeh, todas las respuestas realmente confirman lo que ya crees. –

+0

Gracias por la información adicional, aunque debería haber formulado la pregunta mejor para que se centrara más en la parte de rendimiento y menos en la ventana. notación +1. –

2

Esto puede ser una pregunta de interés si desea saber cómo funciona el proceso Scope Chain and the Identifier Resolution.

La cadena de alcance es una lista de objetos que se buscan al evaluar un identificador, esos objetos no son accesibles por código, solo se puede acceder a sus propiedades (identificadores).

Al principio, en el código global, la cadena de alcance se crea e inicializa para contener solo el objeto global.

Los objetos subsiguientes en la cadena se crean cuando ingresa en el contexto de ejecución de la función y por la instrucción with y la cláusula catch, ambos también introducen objetos en la cadena.

Por ejemplo:

// global code 
var var1 = 1, var2 = 2; 
(function() { // one 
    var var3 = 3; 
    (function() { // two 
    var var4 = 4; 

    with ({var5: 5}) { // three 
     alert(var1); 
    } 
    })(); 
})(); 

En el código anterior, la cadena de ámbito contendrá diferentes objetos en diferentes niveles, por ejemplo, en el nivel más bajo, dentro de la instrucción with, si se utiliza el var1 o var2 variables, la cadena de alcance contendrá 4 objetos que se necesitarán inspeccionar para obtener ese identificador: el introducido por la declaración with, las dos funciones y finalmente el objeto global.

También tienen que saber que window es simplemente una propiedad que existe en el objeto global y apunta al mismo objeto global. window es introducido por los navegadores, y en otros entornos a menudo no está disponible.

En conclusión, cuando usa window, ya que es solo un identificador (no es una palabra reservada ni nada de eso) y necesita pasar todo el proceso de resolución para obtener el objeto global, window.Math necesita un paso que se realiza mediante el acceso de propiedad dot (.).

+0

Gracias por la gran respuesta +1 –

1

Por lo que yo entiendo la lógica de JavaScript, todo lo que refiere como something se busca en el ámbito de variable global. En las implementaciones del navegador, el objeto windowes el objeto global. Por lo tanto, cuando está pidiendo window.Math, en realidad tiene que des-referenciar lo que significa window, luego obtener sus propiedades y encontrar Math allí. Si simplemente solicita Math, el primer lugar donde se busca es el objeto global.

Por lo tanto, sí, la llamada a Math.something será más rápida que window.Math.something.

D. Crockeford habla de ello en su conferencia http://video.yahoo.com/watch/111593/1710507, por lo que recuerdo, está en la tercera parte del video.

Cuestiones relacionadas