Por qué es la Función.prototipo.¿enlazar despacio?
Al comparar este punto de referencia con chrome 16 vs opera 11.6 encontramos que
- en chrome native bind es casi 5 veces más lento que una versión emulada de bind
- en opera native bind es casi 4 veces más rápido que una versión emulada de bind
Donde una versión emulada de bind en este caso es
var emulatebind = function (f, context) {
return function () {
f.apply(context, arguments);
};
};
¿Hay buenas razones por las que hay tal diferencia o es solo una cuestión de que v8 no optimice lo suficiente?
Nota: que emulatebind
solo implementa un subconjunto, pero eso no es realmente relevante. Si tiene un enlace emulado completamente equipado y optimizado, la diferencia de rendimiento en el índice de referencia sigue existiendo.
3 answers
Basado en http://jsperf.com/bind-vs-emulate/6 , que agrega la versión es5-shim para la comparación, parece que el culpable es la rama extra y instanceof
que la versión enlazada tiene que realizar para probar si está siendo llamada como un constructor.
Cada vez que se ejecuta la versión enlazada, el código que se ejecuta es esencialmente:
if (this instanceof bound) {
// Never reached, but the `instanceof` check and branch presumably has a cost
} else {
return target.apply(
that,
args.concat(slice.call(arguments))
);
// args is [] in your case.
// So the cost is:
// * Converting (empty) Arguments object to (empty) array.
// * Concating two empty arrays.
}
En el código fuente V8 , esta comprobación aparece (dentro de boundFunction
) como
if (%_IsConstructCall()) {
return %NewObjectFromBound(boundFunction);
}
(Enlace de texto plano a v8natives.js para cuando la búsqueda de código de Google muere.)
Es un poco desconcertante que, para Chrome 16 al menos, la versión es5-shim sigue siendo más rápido que la versión nativa. Y que otros navegadores tienen resultados bastante variables para es5-shim vs. nativo. Especulación: tal vez %_IsConstructCall()
es incluso más lento que this instanceof bound
, tal vez debido a cruzar los límites del código nativo/JS. Y tal vez otros navegadores tienen una forma mucho más rápida de comprobar si hay una llamada [[Construct]]
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-06 21:10:10
Es imposible implementar un bind
completo solo en ES5. En secciones particulares15.3.4.5.1 hasta 15.3.4.5.3 de la especificación no puede ser emulado.
15.3.4.5.1, en particular, parece una posible carga de rendimiento: en funciones de enlace corto tienen diferentes propiedades internas [[Call]]
, por lo que llamarlas es probable que tome una ruta de código inusual y posiblemente más complicada.
Varias otras características específicas no emulables de una función enlazada (tales como arguments
/caller
envenenamiento, y posiblemente la costumbre length
independiente de la firma original) posiblemente podría agregar sobrecarga a cada llamada, aunque admito que es un poco improbable. Aunque parece que V8 ni siquiera implementa el envenenamiento en este momento.
EDITAR esta respuesta es especulación, pero mi otra respuesta tiene algo más cercano a la evidencia. Todavía creo que esto es una especulación válida, pero es una respuesta separada, así que lo dejaré como tal y solo lo referiré a la el otro.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-08 16:38:20
El código fuente V8 para bind está implementado en JS.
El OP no emula bind
porque no curry argumentos como bind
lo hace. Aquí está un completo bind
:
var emulatebind = function (f, context) {
var curriedArgs = Array.prototype.slice.call(arguments, 2);
return function () {
var allArgs = curriedArgs.slice(0);
for (var i = 0, n = arguments.length; i < n; ++i) {
allArgs.push(arguments[i]);
}
return f.apply(context, allArgs);
};
};
Obviamente, una optimización rápida sería hacer
return f.apply(context, arguments);
En cambio si curriedArgs.length == 0
, porque de lo contrario tiene dos creaciones de matrices innecesarias, y una copia innecesaria, pero tal vez la versión nativa está realmente implementada en JS y no hace esa optimización.
Advertencia: Este completo bind
no maneja correctamente algunos casos de esquina alrededor de this
argumento coerción en modo estricto. Eso podría ser otra fuente de gastos generales.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-06 19:46:49