Большие подстроки ~ 9000x быстрее в Firefox, чем Chrome: почему?
Итак, я запускаю свой первый клиентский проект на основе 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. Я обеспокоен попыткой улучшить свои навыки и узнать этот язык на более глубоком уровне.
Ответы
Ответ 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
.