2015-10-01 2 views
11

Рассмотрим этот код:Почему запуск этого цикла 9 раз занимает 100 раз больше, чем запуск 8 раз?

Test = function() { 
} 

t = new Test(); 

for (var i = 0; i < 8; i++) { 
    result = t instanceof Test; 
} 

Если вы изменяете число итераций от к , петля вдруг займет около 100 раз больше завершить в последней версии Firefox (41,0 0,1). Я испытал это на двух разных компьютерах и магический предел всегда 8.

Вот тест JSPerf, который я использовал: http://jsperf.com/instanceof-8-times-vs-9-times

Кто-нибудь есть какие-либо идеи, почему это могло произойти? Это, по-видимому, относится к instanceof. Это не происходит, если вы делаете что-то еще с объектом, например, проверяете свойство.


Примечание: Я также подал Bugzilla bug об этом.

+1

Интересно, что этого не происходит, когда вы делаете это inline 8/9 раз: http://jsperf.com/instanceof-8-times-vs-9-times/3. Также не происходит в Firefox 40.3. – ralh

+1

Выполнение рекурсивного цикла в функцию, по-видимому, быстрее. Необычный. – Chris

+1

Спасибо за открытие ошибки для этой проблемы (https://bugzilla.mozilla.org/show_bug.cgi?id=1210342). Мое слепое предположение заключалось в том, что это всего лишь затраты на компиляцию базового компилятора, которые появляются, когда мы вводим цикл за дополнительное время. Если вы будете использовать большее количество итераций, стоимость каждой итерации должна быть амортизирована. – nbp

ответ

-2

Когда значение равно 9, вы повторяете 10 раз, поэтому 100x предположительно 10^2 - то есть два символа, а не один. Можно также определить, будет ли это делать 100 раз, что приведет к замедлению 10^3. Звучит орехи, но этот Javascript.

1

Jan de Mooij из команды Mozilla опубликовал некоторые данные в Bugzilla thread. Вот моя упрощенная интерпретация его сугубо технических ответов:

В i < 8 случае Firefox достаточно умен, чтобы поднять result = t instanceof Test; заявления из цикла (по моим тестамам, это, кажется, не пропустить его вообще). В случае i < 9, очевидно, не делает эту оптимизацию.

Почему? Причина не совсем ясна, но, вероятно, это связано с тем, что 9 итераций - это порог, выше которого функция считается «горячей», достаточной для ее запуска через JIT-компилятор. Шкаф i < 8 остается в интерпретаторе. (Я не понимаю, почему JIT-ing исключает подъем, но, по-видимому, он работает в текущей версии двигателя.)

Интересно, что порог 8-итераций не кажется универсальным. Например, если мы заменим наш собственный прототип (Test) со встроенным в прототипе (например CustomEvent), подъемное, кажется, не происходит, независимо от количества итераций (relevant JSPerf):

for (var i = 0; i < 8; i++) { //or i < 9 
    t instanceof CustomEvent; 
} 

Возвращаясь к исходный код с использованием прототипа Test, почему производительность настолько плоха в случае i < 9? Это связано с тем, как работает JSPerf. Код «setup» выполняется не только один раз - он запускается один раз «за тест». Каждый раз, когда вы нажимаете «Выполнить», JSPerf запускает сотни «тестов», каждый из которых содержит тысячи итераций. Таким образом, установочный код запускается сотни раз.Это означает, что существуют сотни различных объектов прототипа имени Test в программе, все созданные с линией:

Test = function(){ 
} 

Ион JIT оптимизирующий компилятор может легко оптимизировать случай, в котором мы используем instanceof на том же объекте прототипе много раз (как у нас CustomEvent в this test case), но , когда он замечает, что существует более одного объекта с тем же именем, по-видимому, он бросает руки в воздух.

Jan справедливо указал, что это вряд ли повлияет на слишком много сценариев реального мира, поскольку обычно один идентификатор связан с одним прототипом объекта (например, у вас есть класс Foobar, который определяется только один раз и никогда не повторяет -определенный). Но JSPerf повторно определяет прототипы сотни раз. Мне кажется, что этот факт вызывает серьезные сомнения во всех опубликованных результатах JSPerf, которые включают в себя определения прототипов, за исключением тех, которые явно избегают переопределения с использованием глобальных переменных (как в этом test case), что, пожалуй, является самым важным заключением из всего этого.

Например, тесты JSPerf, связанные с этим вопросом: Is using instanceof operator in javascript a performance issue?, вероятно, бесполезны, поскольку все они определяют прототипы в установочном коде.

Смежные вопросы