La principale différence entre le cas n° 4 et tous les autres est que la première fois que vous utilisez une fermeture comme constructeur est toujours assez coûteuse.
-
Elle est toujours traitée dans le runtime V8 (et non dans le code généré) et la transition entre le code JS compilé et le runtime C++ est assez coûteuse. Les allocations ultérieures sont généralement traitées dans le code généré. Vous pouvez jeter un coup d'oeil à Generate_JSConstructStubHelper
en builtins-ia32.cc
et remarquez qu'il tombe dans le Runtime_NewObject
lorsque la fermeture n'a pas de carte initiale. (voir http://code.google.com/p/v8/source/browse/trunk/src/ia32/builtins-ia32.cc#138 )
-
Lorsque closure est utilisé comme constructeur pour la première fois, V8 doit créer une nouvelle carte (aka classe cachée) et l'assigner en tant que carte initiale pour cette fermeture. Voir http://code.google.com/p/v8/source/browse/trunk/src/heap.cc#3266 . Ce qui est important ici, c'est que les cartes sont allouées dans un espace mémoire séparé. Cet espace ne peut pas être nettoyé par une opération partielle rapide. fouiller collecteur. Lorsque l'espace de la carte déborde, V8 doit effectuer des opérations complètes relativement coûteuses. balayage des marques GC.
Il y a quelques autres choses qui se produisent lorsque vous utilisez la fermeture comme constructeur pour la première fois mais 1 et 2 sont les principaux contributeurs à la lenteur du scénario de test #4.
Si nous comparons les expressions #1 et #4 alors les différences sont :
-
1 n'alloue pas une nouvelle fermeture à chaque fois ;
-
Le numéro 1 n'entre pas dans le temps d'exécution à chaque fois : après la fermeture, la construction de la carte initiale est traitée dans le chemin rapide du code généré. Gérer l'ensemble de la construction dans le code généré est beaucoup plus rapide que de faire des allers-retours entre le runtime et le code généré ;
-
1 n'alloue pas à chaque fois une nouvelle carte initiale pour chaque nouvelle fermeture ;
-
Le numéro 1 ne provoque pas de balayage en débordant de l'espace de la carte (seulement des récupérations bon marché).
Si nous comparons les numéros 3 et 4, les différences sont les suivantes :
-
3 n'alloue pas à chaque fois une nouvelle carte initiale pour chaque nouvelle fermeture ;
-
Le n°3 ne provoque pas de balayage en débordant de l'espace de la carte (seulement des récupérations bon marché) ;
-
Le n°4 fait moins sur le plan JS (pas de Function.prototype.call, pas d'Object.create, pas de Object.prototype lookup, etc.) et plus sur le plan C++ (le n°3 entre également dans le runtime à chaque fois que vous faites Object.create, mais ne fait pas grand-chose).
L'essentiel ici est que la première fois que vous utilisez la fermeture comme constructeur est coûteuse par rapport aux appels de construction ultérieurs de l'option même fermeture parce que V8 doit installer de la plomberie. Si nous éliminons immédiatement la fermeture, nous jetons tout le travail que V8 a fait pour accélérer les appels de constructeurs ultérieurs.
3 votes
Par curiosité, quel est l'intérêt de l'utilisation de
Object.create(Object.prototype)
plutôt que d'utiliser un objet littéral({})
? Ne sont-elles pas exactement les mêmes ? Cela pourrait-il être la source d'une certaine différence de performance ?0 votes
@maerics Je me suis senti
Object.create(object.prototype)
était plus dans "l'esprit" denew
. Il semble que{}
est plus rapide dans Chrome, plus lente dans FF et à peu près la même chose dans IE.0 votes
Bien que je puisse vaguement voir pourquoi
Object.create(Object.prototype)
est plus dans l'esprit denew
Je serais intéressé de voir un exemple qui essaie de construire plus qu'un simple objet vide afin de confirmer cela.0 votes
@Domenic Benchmark pour {} vs Object.create En dehors du cas simple dans firefox, les littéraux sont plus rapides.
0 votes
Mais... qu'en est-il
new
? En d'autres termes, à quoi ressemble le code pour accomplir la même chose avecnew
? (Je peux en quelque sorte le deviner, mais le fait que vous le précisiez pourrait faire apparaître plus clairement les différences).0 votes
@Domenic Un autre benchmark . On dirait que
new
est plus rapide pour tout ce qui n'est pas trivial. Je suis juste curieux de savoir quels sont les coûts qui le rendent plus lent pour les cas triviaux.0 votes
La dernière phrase sur le C++ est déroutante et facile à confondre avec de l'ironie, une phrase où le mot "pas" est absent ou une exagération. Préféreriez-vous une explication qui inclut le C++ ou non ?
1 votes
@Alexander merci pour cela. Je l'ai supprimé pour être plus clair.