производительность — оптимизируют ли движки JavaScript константы, определенные в замыканиях?

Оптимизируют ли движки JavaScript константы, определенные в замыканиях?

Разработчик V8 здесь. Ваша интуиция верна.

TL; DR: inlinedAccess каждый раз создает новый объект. constantAccess более эффективен, потому что он избегает воссоздания объекта при каждом вызове. Для еще лучшей производительности используйте Map .

Тот факт, что «быстрый тест» дает одинаковое время для обеих функций, показывает, насколько легко микробенчмарки могут вводить в заблуждение ;-)

  • Создание объектов, подобных объекту в вашем примере, выполняется довольно быстро, поэтому влияние сложно измерить. Вы можете усилить влияние повторного создания объекта, сделав его более дорогим, например, заменив одно свойство на b: new Array(100),
  • Преобразование числа в строку и последующая конкатенация строк в 'result: ' ... вносят значительный вклад в общее время; Вы можете оставить это, чтобы получить более четкий сигнал.
  • Для небольшого теста вы должны быть осторожны, чтобы компилятор не оптимизировал все. Присвоение результата глобальной переменной делает свое дело.
  • Это также имеет огромное значение, независимо от того, ищите ли вы одно и то же свойство или разные свойства. Поиск объектов в JavaScript не совсем простая (== быстрая) операция; V8 имеет очень быструю стратегию оптимизации / кэширования, когда у данного сайта всегда одно и то же свойство (и одна и та же форма объекта), но для изменяющихся свойств (или форм объекта) он должен выполнять более дорогостоящий поиск.
  • Поиск на Map для различных ключей выполняется быстрее, чем поиск свойств объекта. Использование объектов в качестве карт — это 2010 год, современный JavaScript имеет правильные Map , так что используйте их! :-)
  • Array элементов Array еще быстрее, но, конечно, вы можете использовать их только тогда, когда ваши ключи целые.
  • Когда количество возможных ключей, которые нужно найти, мало, операторы switch трудно превзойти. Они не очень хорошо подходят для большого количества ключей.

Давайте вложим все эти мысли в код:

 function inlinedAccess(key) { const inlinedLookupTable = { a: 1, b: new Array(100), c: 3, d: 4, } return inlinedLookupTable[key]; } const CONSTANT_TABLE = { a: 1, b: new Array(100), c: 3, d: 4, } function constantAccess(key) { return CONSTANT_TABLE[key]; } const LOOKUP_MAP = new Map([ ["a", 1], ["b", new Array(100)], ["c", 3], ["d", 4] ]); function mapAccess(key) { return LOOKUP_MAP.get(key); } const ARRAY_TABLE = ["a", "b", "c", "d"] function integerAccess(key) { return ARRAY_TABLE[key]; } function switchAccess(key) { switch (key) { case "a": return 1; case "b": return new Array(100); case "c": return 3; case "d": return 4; } } const kCount = 10000000; let result = null; let t1 = Date.now(); for (let i = 0; i {amp}lt; kCount; i  ) { result = inlinedAccess("a"); result = inlinedAccess("d"); } let t2 = Date.now(); for (let i = 0; i {amp}lt; kCount; i  ) { result = constantAccess("a"); result = constantAccess("d"); } let t3 = Date.now(); for (let i = 0; i {amp}lt; kCount; i  ) { result = mapAccess("a"); result = mapAccess("d"); } let t4 = Date.now(); for (let i = 0; i {amp}lt; kCount; i  ) { result = integerAccess(0); result = integerAccess(3); } let t5 = Date.now(); for (let i = 0; i {amp}lt; kCount; i  ) { result = switchAccess("a"); result = switchAccess("d"); } let t6 = Date.now(); console.log("inlinedAccess: "   (t2 - t1)); console.log("constantAccess: "   (t3 - t2)); console.log("mapAccess: "   (t4 - t3)); console.log("integerAccess: "   (t5 - t4)); console.log("switchAccess: "   (t6 - t5)); 

Я получаю следующие результаты:

 inlinedAccess: 1613 constantAccess: 194 mapAccess: 95 integerAccess: 15 switchAccess: 9 

Все, что сказало: эти числа «миллисекунды для 10-миллиметровых поисков». В реальных приложениях различия, вероятно, слишком малы, чтобы иметь значение, поэтому вы можете написать любой код, который будет наиболее читаемым / поддерживаемым / и т.д. Например, если вы выполняете только 100К-поиски, результаты:

 inlinedAccess: 31 constantAccess: 6 mapAccess: 6 integerAccess: 5 switchAccess: 4 

Кстати, распространенным вариантом этой ситуации является создание / вызов функций. Этот:

 function singleton_callback(...) { ... } function efficient(...) { return singleton_callback(...); } 

гораздо эффективнее, чем это:

 function wasteful(...) { function new_callback_every_time(...) { ... } return new_callback_every_time(...); } 

И аналогично, это:

 function singleton_method(args) { ... } function EfficientObjectConstructor(param) { this.___ = param; this.method = singleton_method; } 

гораздо эффективнее, чем это:

 function WastefulObjectConstructor(param) { this.___ = param; this.method = function(...) { // Allocates a new function every time. }; } 

(Конечно, обычным способом сделать это является Constructor.prototype.method = function(...) {...} , который также избегает повторного создания функций. В настоящее время вы можете просто использовать class es.)

Понравилась статья? Поделиться с друзьями:
JavaScript & TypeScript
Adblock
detector