32 votes

Pourquoi Function.prototype.bind est-il lent?

Lors de la comparaison de cet indice de référence avec chrome 16 vs opéra de 11,6 nous constatons que

  • dans google chrome, natif de bind est presque 5 fois plus lent que d'une version émulée de bind
  • à l'opéra, natif de bind est près de 4 fois plus rapide, puis une version émulée de bind

Lorsqu'une version émulée de bind dans ce cas est

var emulatebind = function (f, context) {
    return function () {
        f.apply(context, arguments);
    };
};

Sont là de bonnes raisons pourquoi il ya une telle différence ou est-ce juste une question de v8 ne pas optimiser suffisant?

Remarque: emulatebind seulement implémente un sous-ensemble, mais ce n'est pas vraiment pertinent. Si vous avez un complet et optimisé émulé lier la différence de performances de l'indice de référence existe toujours.

27voto

Domenic Points 40761

Basé sur http://jsperf.com/bind-vs-emulate/6qui ajoute l'es5-shim version pour la comparaison, il semble que le coupable est le supplément de la branche et de l' instanceof qui le lie à effectuer pour tester si il est appelé en tant que constructeur.

Chaque fois lié à la version est exécuté, le code qui est exécuté est essentiellement:

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.
}

Dans le V8 de code source, cette case s'affiche (à l'intérieur d' boundFunction) que

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

(Lien vers le texte clair v8natives.js quand Google Recherche de Code meurt.)

C'est un peu déroutant de constater que, pour Chrome de 16 ans au moins, l'es5-shim version est encore plus rapide que la version native. Et que les autres navigateurs ont plutôt des résultats variables pour es5-shim vs natif. La spéculation: peut - %_IsConstructCall() est encore plus lente que this instanceof bound, peut-être due à la traversée de natif/code JS limites. Et peut-être d'autres navigateurs ont un moyen beaucoup plus rapide de vérifier l' [[Construct]] appel.

7voto

Domenic Points 40761

Il est impossible de mettre en œuvre un complet bind dans l'ES5 seul. En particulier dans les sections 15.3.4.5.1 par 15.3.4.5.3 de la spec ne peut pas être imité.

15.3.4.5.1, en particulier, apparaît comme une performance possible fardeau: en bref lié fonctions différentes, [[Call]] propriétés internes, afin de l'appelant est susceptible de prendre une inhabituelle et peut-être plus compliqué chemin d'accès du code.

Divers autres spécifiques de l'onu-emulatable dispose d'un encadrement de la fonction (comme arguments/caller d'empoisonnement, et, éventuellement, la coutume length indépendant de la signature originale) pourrait peut-être ajouter des frais généraux pour chaque appel, même si j'avoue que c'est un peu improbable. Bien qu'il ressemble V8 n'a même pas de mettre en œuvre l'empoisonnement à l'heure actuelle.

MODIFIER cette réponse, c'est de la spéculation, mais mon autre réponse a quelque chose de plus approchant de la preuve. Je pense toujours que c'est valable à la spéculation, mais c'est une réponse distincte, donc je vais le laisser comme ça et juste de vous référer à l'autre.

3voto

Mike Samuel Points 54712

Le V8 de code source pour lier est mis en œuvre en JS.

L'OP n'a pas émuler bind car il n'a pas de curry arguments de la façon dont bind n'. Ici est entièrement comporté 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);
  };
};

De toute évidence, un rapide coup d'optimisation serait de le faire

return f.apply(context, arguments);

au lieu de cela, si curriedArgs.length == 0, parce que sinon vous avez deux inutiles tableau créations, et une copie superflue, mais peut-être que la version native est vraiment mis en œuvre en JS et ne pas faire que de l'optimisation.

Mise en garde: Ce complet, bind ne gère pas correctement certains cas du coin autour d' this argument de la contrainte en mode strict. Que pourrait être une autre source de surcharge.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X