Большие подстроки в Firefox в 9000 раз быстрее, чем в Chrome: почему? - PullRequest
19 голосов
/ 01 июня 2011

Тест: 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 ]

15 голосов
/ 01 июня 2011

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

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

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

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

1 голос
/ 01 июня 2011

Вы исключили чтение .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) из частей массива.

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

EDIT:

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

...