Grandes sous-chaînes ~ 9000x plus rapides dans Firefox que Chrome: pourquoi?

L'indice de référence: http://jsperf.com/substringing

Je commence donc mon premier projet client-basé sur le navigateur HTML5. Il faudra analyser très, très gros fichiers texte dans, essentiellement, un tableau ou des tableaux d'objets. Je sais comment je vais le coder; Mon principal souci est maintenant d'obtenir le code de l'analyseur aussi vite que possible, et mon premier test est Chrome. Cependant, en regardant les différences entre les méthodes de sous-chaîne (je n'ai pas touché JavaScript depuis longtemps, longtemps), j'ai remarqué que cette référence était incroyablement lente dans Chrome par rapport à FireFox. Pourquoi?

Ma première hypothèse est qu'il s'agit de la façon dont le moteur JS de FireFox manipulerait des objets de chaîne et que, pour FireFox, cette opération est une simple manipulation de pointeur, tandis que pour Chrome, il est en fait de copies papier. Mais, je ne sais pas pourquoi Chrome ne ferait pas de manipulation de pointeur ou pourquoi FireFox le ferait . Quelqu'un at-il une idée?

JSPerf semble jeter mes résultats FireFox, ne pas les afficher sur BrowserScope. Pour moi, je reçois 9 568 203 ± 1,44% Ops / sec sur .substr() dans FF4.

Edit: Donc, je vois un résultat de performance FF3.5 là-bas en bas de Chrome. J'ai donc décidé de tester l'hypothèse de mes pointeurs. Cela m'a amené à une 2ème révision de mon test de Substrings, qui fait 1 1,092,718±1.62% Ops / sec en FF4 contre 1,195±3.81% Ops / sec en Chrome, jusqu'à 1000 fois plus rapide, mais encore une différence inexplicable dans les performances.

Un postscriptum: non, je ne suis pas concerné par une lèche sur Internet Explorer. Je suis préoccupé d'essayer d'améliorer mes compétences et de connaître cette langue à un niveau plus profond.

Dans le cas de Spidermonkey (le moteur JS dans Firefox), un appel substring() crée simplement une nouvelle "chaîne dépendante": un objet chaîne qui stocke un pointeur sur la chose, c'est une sous-chaîne et les décalages de début et de fin. C'est précisément pour rendre la substring() rapide, et une optimisation évidente compte tenu des chaînes immuables.

En ce qui concerne pourquoi V8 ne le fait pas … Une possibilité est que V8 tente d'économiser de l'espace: dans la configuration de chaîne dépendante si vous vous attenez à la sous-chaîne, mais oubliez la chaîne d'origine, la chaîne d'origine ne peut pas être GCed car la La sous-chaîne utilise une partie de ses données de chaîne.

En tout cas, j'ai juste regardé la source V8, et on dirait qu'ils ne font tout simplement aucune sorte de chaînes dépendantes; Les commentaires n'expliquent pas pourquoi ils ne le font pas.

[Update, 12/2013]: Quelques mois après, j'ai donné la réponse ci-dessus. V8 a ajouté un support pour les chaînes dépendantes, comme le souligne Paul Draper.

Avez-vous éliminé la lecture de .length rapport à vos résultats de référence?

Je crois que V8 a quelques représentations d'une chaîne:

 1. a sequence of ASCII bytes 2. a sequence of UTF-16 code units. 3. a slice of a string (result of substring) 4. a concatenation of two strings. 

Le numéro 4 est ce qui rend string += efficace.

Je suppose que, mais s'ils essaient d'emballer deux pointeurs de chaîne et une longueur dans un petit espace, ils peuvent ne pas pouvoir mettre en cache de grandes longueurs avec les pointeurs, donc peut-être finir par marcher dans la liste de liens pour calculer la durée. Cela suppose bien sûr que Array.prototype.join crée des chaînes de forme (4) à partir des parties du tableau.

Cela conduit à une hypothèse vérifiable qui expliquerait l'écart, même en l'absence de copies de tampon.

MODIFIER:

J'ai regardé le code source V8 et StringBuilderConcat est l'endroit où je commencerais à tirer, en particulier runtime.cc .