Подтвердить что ты не робот

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

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

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

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

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

Изменить: Таким образом, я вижу результат производительности FF3.5, находящийся на самом деле ниже Chrome. Поэтому я решил проверить гипотезу о моих указателях. Это привело меня к 2nd revision моего теста подстрок, который делает 1,092,718±1.62% Ops/sec в FF4 в сравнении с 1,195±3.81% Ops/sec в Chrome, до 1000 раз быстрее, но все же необъяснимое различие в производительности.

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Удалили ли вы чтение .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.