Большие подстроки ~ в 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
,