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

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

Рассмотрим этот код:

Test = function() {
}

t = new Test();

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

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

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

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


Примечание. Я также написал ошибку Bugzilla об этом.

4b9b3361

Ответ 1

Ян де Моой из команды Mozilla опубликовал некоторые подробности в поток Bugzilla. Здесь моя упрощенная интерпретация его высокотехнических ответов:

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

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

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

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

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

Test = function(){
}

Компилятор оптимизации Ion JIT может легко оптимизировать случай, когда мы много раз используем instanceof на одном и том же объекте-прототипе (как и в CustomEvent в этот тестовый сценарий), но , когда он замечает, что существует более одного объекта с тем же именем, видимо, он бросает руки в воздух.

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

Например, тесты JSPerf связаны с этим вопросом: Использует ли оператор instanceof в javascript проблему с производительностью?, вероятно, бесполезен, поскольку все они определяют прототипы в настройке код.

Ответ 2

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