var toSizeString = (function() {
var KB = 1024.0,
MB = 1024 * KB,
GB = 1024 * MB;
return function(size) {
var gbSize = size / GB,
gbMod = size % GB,
mbSize = gbMod / MB,
mbMod = gbMod % MB,
kbSize = mbMod / KB;
if (Math.floor(gbSize)) {
return gbSize.toFixed(1) + 'GB';
} else if (Math.floor(mbSize)) {
return mbSize.toFixed(1) + 'MB';
} else if (Math.floor(kbSize)) {
return kbSize.toFixed(1) + 'KB';
} else {
return size + 'B';
}
};
})();
Y la función más rápida: (tenga en cuenta que siempre debe calcular las mismas variables kb / mb / gb una y otra vez). ¿Dónde gana rendimiento?
function toSizeString (size) {
var KB = 1024.0,
MB = 1024 * KB,
GB = 1024 * MB;
var gbSize = size / GB,
gbMod = size % GB,
mbSize = gbMod / MB,
mbMod = gbMod % MB,
kbSize = mbMod / KB;
if (Math.floor(gbSize)) {
return gbSize.toFixed(1) + 'GB';
} else if (Math.floor(mbSize)) {
return mbSize.toFixed(1) + 'MB';
} else if (Math.floor(kbSize)) {
return kbSize.toFixed(1) + 'KB';
} else {
return size + 'B';
}
};
javascript
performance
Para mi
fuente
fuente
Respuestas:
Todos los motores JavaScript modernos compilan justo a tiempo. No se pueden hacer presunciones sobre lo que "debe crear una y otra vez". Ese tipo de cálculo es relativamente fácil de optimizar, en cualquier caso.
Por otro lado, cerrar sobre variables constantes no es un caso típico para el que apuntaría la compilación JIT. Por lo general, crea un cierre cuando desea poder cambiar esas variables en diferentes invocaciones. También está creando una desreferencia de puntero adicional para acceder a esas variables, como la diferencia entre acceder a una variable miembro y un int local en OOP.
Este tipo de situación es la razón por la cual la gente tira la línea de "optimización prematura". Las optimizaciones fáciles ya están hechas por el compilador.
fuente
Las variables son baratas. Los contextos de ejecución y las cadenas de alcance son caros.
Hay varias respuestas que esencialmente se reducen a "porque cierres", y esas son esencialmente ciertas, pero el problema no es específicamente con el cierre, es el hecho de que tiene una función que hace referencia a variables en un ámbito diferente. Tendría el mismo problema si estas fueran variables globales en el
window
objeto, a diferencia de las variables locales dentro del IIFE. Pruébalo y verás.Entonces, en su primera función, cuando el motor ve esta declaración:
Tiene que seguir los siguientes pasos:
size
en el alcance actual. (Lo encontré.)GB
en el alcance actual. (Extraviado.)GB
en el ámbito primario. (Lo encontré.)gbSize
.El paso 3 es considerablemente más costoso que simplemente asignar una variable. Además, haces esto cinco veces , incluyendo dos veces para ambos
GB
yMB
. Sospecho que si los alias al principio de la función (por ejemplovar gb = GB
) y haces referencia al alias, en realidad produciría una pequeña aceleración, aunque también es posible que algunos motores JS ya realicen esta optimización. Y, por supuesto, la forma más efectiva de acelerar la ejecución es simplemente no atravesar la cadena de alcance.Tenga en cuenta que JavaScript no es como un lenguaje compilado, de tipo estático, donde el compilador resuelve estas direcciones variables en tiempo de compilación. El motor JS tiene que resolverlos por nombre , y estas búsquedas ocurren en tiempo de ejecución, siempre. Por lo tanto, debe evitarlos cuando sea posible.
La asignación de variables es extremadamente barata en JavaScript. En realidad, podría ser la operación más barata, aunque no tengo nada que respalde esa afirmación. Sin embargo, es seguro decir que casi nunca es una buena idea tratar de evitar crear variables; casi cualquier optimización que intente hacer en esa área terminará empeorando las cosas, en términos de rendimiento.
fuente
var a, b, c
lo tanto, dado que podemos accederb
comoscope[1]
. Todos los ámbitos están numerados, y si este ámbito está anidado a cinco ámbitos de profundidad, entoncesb
se aborda por completo loenv[5][1]
que se conoce durante el análisis. En el código nativo, los ámbitos corresponden a segmentos de pila. Los cierres son más complicados ya que deben respaldar y reemplazar elenv
*(scope->outer + variable_offset)
para un acceso; cada nivel de alcance de función adicional cuesta una desreferencia de puntero adicional. Parece que ambos teníamos razón :)Un ejemplo implica un cierre, el otro no. Implementar cierres es un poco complicado, ya que las variables cerradas no funcionan como las variables normales. Esto es más obvio en un lenguaje de bajo nivel como C, pero usaré JavaScript para ilustrar esto.
Un cierre no solo consiste en una función, sino también en todas las variables que cerró. Cuando queremos invocar esa función, también debemos proporcionar todas las variables cerradas. Podemos modelar un cierre por una función que recibe un objeto como primer argumento que representa estas variables cerradas:
Tenga en cuenta la convención de llamadas incómodas que
closure.apply(closure, ...realArgs)
esto requiereEl soporte de objetos incorporados de JavaScript hace posible omitir el
vars
argumento explícito , y nos permite usarthis
en su lugar:Esos ejemplos son equivalentes a este código que en realidad usa cierres:
En este último ejemplo, el objeto solo se usa para agrupar las dos funciones devueltas; La
this
unión es irrelevante. El lenguaje se ocupa de todos los detalles de hacer posibles los cierres: pasar datos ocultos a la función real, cambiar todos los accesos a las variables de cierre a búsquedas en esos datos ocultos.Pero llamar a los cierres implica la sobrecarga de pasar esos datos adicionales, y ejecutar un cierre implica la sobrecarga de las búsquedas en esos datos adicionales, empeorado por la mala ubicación de la caché y generalmente una desferencia de puntero en comparación con las variables ordinarias, por lo que no es sorprendente que Una solución que no se basa en cierres funciona mejor. Especialmente porque todo lo que su cierre le ahorra hacer son algunas operaciones aritméticas extremadamente baratas, que incluso podrían doblarse constantemente durante el análisis.
fuente