Pregunta ¿Por qué Function.prototype.bind es lento?


Al comparar este punto de referencia con Chrome 16 vs Opera 11.6 encontramos que

  • en el enlace nativo de cromo es casi 5 veces más lento que una versión emulada de enlace
  • en enlace nativo de opera es casi 4 veces más rápido que una versión emulada de enlace

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 cuales existe tal diferencia o es solo una cuestión de v8 no optimizar lo suficiente?

Tenga en cuenta que emulatebind solo implementa un subconjunto pero eso no es realmente relevante. Si tiene un emulado optimizado y con todas las funciones, diferencia de rendimiento en el punto de referencia todavía existe.


32
2017-12-28 12:49


origen


Respuestas:


Residencia en http://jsperf.com/bind-vs-emulate/6, que agrega la versión es5-shim para comparar, parece que el culpable es la rama extra y instanceof que la versión enlazada tiene que realizar para probar si se está llamando 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, aparece esta marca (dentro boundFunction) como

if (%_IsConstructCall()) {
    return %NewObjectFromBound(boundFunction);
}

(Enlace de texto simple a v8natives.js para cuando muera Google Code Search)

Es un poco desconcertante que, al menos para Chrome 16, la versión de es5-shim sea aún más rápida que la versión nativa. Y que otros navegadores tienen resultados bastante variables para es5-shim vs. native. Especulación: tal vez %_IsConstructCall() es incluso más lento que this instanceof bound, tal vez debido al cruce de fronteras de código nativo / JS. Y quizás otros navegadores tengan una forma mucho más rápida de verificar [[Construct]] llamada.


27
2018-01-06 20:42



Es imposible implementar un sistema completo bind en ES5 solo. En secciones particulares 15.3.4.5.1 hasta 15.3.4.5.3 de la especificación no se puede emular.

15.3.4.5.1, en particular, parece una posible carga de rendimiento: en pocas funciones vinculadas tienen diferentes [[Call]] propiedades internas, por lo que llamarlos 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 (como arguments/caller envenenamiento, y posiblemente la costumbre length independiente de la firma original) posiblemente podría agregar sobrecarga para cada llamada, aunque admito que es un poco improbable. Aunque parece que V8 ni siquiera implementa la intoxicación en este momento.

EDITAR esta respuesta es especulación, pero mi otra respuesta tiene algo más de evidencia que se aproxima. Sigo pensando que esto es una especulación válida, pero es una respuesta separada, así que lo dejo como tal y solo lo remito al otro.


7
2018-01-06 20:28



los Código fuente V8 para enlace está implementado en JS.

El OP no emula bind porque no curry argumentos el camino bind hace. Aquí hay una presentación completa 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 matriz innecesarias y una copia innecesaria, pero tal vez la versión nativa realmente se implemente en JS y no hace esa optimización.

Advertencia: Esta completamente presentado bind no maneja correctamente algunos casos de esquina alrededor this coerción argumento en modo estricto. Esa podría ser otra fuente de gastos generales.


3
2018-01-06 19:35