Большие подстроки ~ в 9000 раз быстрее в Firefox, чем в Chrome: почему?

Тест: http://jsperf.com/substringing

Итак, я запускаю свой самый первый клиентский проект на основе браузера HTML5. Он должен будет анализировать очень, очень большие текстовые файлы, по существу, в массив или массивы объектов. Я знаю, как я собираюсь кодировать это; Моя главная задача сейчас - получить код синтаксического анализатора так быстро, как я могу его получить, и моим основным испытательным стендом является Chrome. Однако, глядя на различия между методами подстрок (я не касался JavaScript в течение долгого времени), я заметил, что этот тест был невероятно медленным в Chrome по сравнению с FireFox. Зачем?

Мое первое предположение состоит в том, что это связано с тем, как движок JF в FireFox будет обрабатывать строковые объекты, и что для FireFox эта операция является простой манипуляцией с указателем, а для Chrome она на самом деле делает печатные копии. Но я не уверен, почему Chrome не будет манипулировать указателями или FireFox. У кого-нибудь есть понимание?

JSPerf, похоже, выдает результаты моего FireFox, а не отображает их в BrowserScope. Для меня я получаю 9,568,203 ±1,44% операций в секунду на .substr() в FF4.

Изменить: Итак, я вижу результат производительности FF3.5 внизу на самом деле ниже Chrome. Поэтому я решил проверить свою гипотезу указателей. Это привело меня ко 2-й ревизии моего теста Substrings, который делает 1,092,718±1.62% Число операций в секунду в FF4 против 1,195±3.81% Число операций в секунду в Chrome снижается только в 1000 раз, но при этом разница в производительности остается необъяснимой.

Постскриптум: Нет, меня не интересует ни один вопрос об Internet Explorer. Я беспокоюсь о том, чтобы улучшить свои навыки и узнать этот язык на более глубоком уровне.

2 ответа

Решение

В случае Spidermonkey (движок JS в Firefox), substring() call просто создает новую "зависимую строку": строковый объект, который хранит указатель на то, что это подстрока, и начальное и конечное смещения. Это точно сделать substring() быстро, и это очевидная оптимизация с учетом неизменяемых строк.

Что касается того, почему V8 не делает этого... Возможно, V8 пытается сэкономить место: в настройке зависимой строки, если вы держитесь за подстроку, но забыли исходную строку, исходная строка не может получить GCed, потому что подстрока использует часть своих строковых данных.

В любом случае, я только что посмотрел на источник V8, и похоже, что они просто не делают никаких зависимых строк вообще; комментарии не объясняют, почему они этого не делают.

[Обновление от 12/2013]: Через несколько месяцев после того, как я дал вышеуказанный ответ, в V8 добавлена ​​поддержка зависимых строк, как отмечает Пол Дрейпер.

Вы устранили чтение .length из ваших результатов теста?

Я считаю, что V8 имеет несколько представлений строки:

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.

Номер 4 это то, что делает строку += эффективный.

Я просто догадываюсь, но если они пытаются упаковать два строковых указателя и длину в небольшое пространство, они могут быть не в состоянии кешировать большие длины с указателями, поэтому может закончить обход списка соединенных ссылок для вычисления длина. Это предполагает, конечно, что Array.prototype.join создает строки формы (4) из частей массива.

Это приводит к проверяемой гипотезе, которая объясняет несоответствие даже отсутствующим буферным копиям.

РЕДАКТИРОВАТЬ:

Я просмотрел исходный код V8, и StringBuilderConcat - это то место, где я бы начал тянуть, особенно runtime.cc,

Другие вопросы по тегам