- PVSM.RU - https://www.pvsm.ru -
Картинка, конечно, стронгли анрилейтед
Разные трюки я тестировал на Google Chrome 107.0.5304.107 и Mozilla Firefox 107.0 на Windows 10.
Чтобы результаты всегда были железно воспроизводимыми, я отключил все С-State’ы, ядра зафиксировал на 5 ГГц.
У меня 9900К, это Coffee Lake c AVX256, какие оптимизации применит Jit для вашего процессора — я не знаю, результат на вашем компьютере может отличаться от моего, в т.ч. из-за микроархитектуры процессора.
Скорость парсинга кода тоже входит в бенчмарк, поэтому браузер с быстрым парсером будет впереди.
Есть ли смысл использовать только dot notation? Какова цена выноса лишней переменной?
var array = new Array(65535).fill()
// 3
var a = array.map((x) => x)
var b = array.map((x) => x)
var c = array.map((x) => x)
// 2
var a = array.map((x) => x)
var b = array.map((x) => x).map((x) => x)
// 1
var a = array.map((x) => x).map((x) => x).map((x) => x)
Чтобы узнать, гонят ли браузер память туда-сюда, делаем мы .map
на массив длиной 65535 с нулями внутри. Линк [1].
Хром не заметил разницы, а вот лиса заметила. Применительно к лисе, у лишней переменной есть измеряемый оверхед.
Проверим. Используя разные биндинги — создадим POJO с переменной е
. Потом добавим ему функцию о
и запустим эту функцию. Бенчмарк простой, но движущихся частей много. Линк [2].
var g = { e: [] }
g.o = function(x) { g.e.push(...[1,2,3]) }
g.o()
Код выглядит так, отличаются только биндинги.
Результат неожиданный, но железно воспроизводимый. var
, быстрее.
Если обе конструкции логически одинаковые, они должны строить одно и то же синтаксическое дерево, верно? Давайте проверим.
// switch case
function thing(e) {
switch (e) {
case 0:
return "0";
case 1:
return "1";
case 2:
return "2";
case 3:
return "3";
default:
return "";
}
}
// bounce pattern
function bounce(x)
{
if (x === 0) return "0";
if (x === 1) return "1";
if (x === 2) return "2";
if (x === 3) return "3";
return ""
}
// ternary
function bounce(x) {
return 0 === x ? "0" : 1 === x ? "1" : 2 === x ? "2" : 3 === x ? "3" : "";
}
Вот так выглядит код. Для всех вариантов он одинаков, отличаются только вызовы.
for (let t = 0; 1e5 > t; t++) bounce(0), bounce(2), bounce(6);
Вызов выглядит так. Линк [3].
for (let t = 0; 1e5 > t; t++) bounce("0"), bounce("2"), bounce("");
Тут мы покидываем строку вместо числа. В свитче и if
блоках используется строгое равенство, поэтому свитч выходит только через default
, а if
’ы выходят только последний return
. Линк [4].
bounce(0), bounce(2), bounce(6)
Просто три вызова подряд, никаких циклов. Линк [5].
Похоже, что после первоначальной компиляции лиса не пытается дальше оптимизировать цикл, как это делает хром.
Также лиса, похоже, не строит одно и то же AST, как это делает хром. Рекомендую заменить ваши длинные if
’ы и bounce паттерны на свитчи, чтобы избежать лисиных тормозов.
Для примера возьму из паттернов функционального программирования, когда ты инициализируешь массив, прокидывая лямбду в инициализатор. Просто ради примера, в качестве этой лямбды будет fizzbuzz.
var times = 65535;
function initializer(val, z) {
const i = z % 5 | 0;
return 0 == (z % 3 | 0) ? 0 === i ? "fizzbuzz" : "fizz" : 0 === i ? "buzz" : z;
}
// for i
var b = new Array(times);
for (var i = 0; i < times; i++) {
b[i] = initializer(b[i], i)
}
b
// for push
var c = [];
for (var i = 0; i < times; i++) {
c.push(initializer(c[i], i))
}
c
// Fill Map
new Array(times).fill().map(initializer)
Это не самый красивый fizzbuzz, но это мой fizzbuzz. Линк на бенч [6].
Вариант с fill map
создаёт два массива, сначала при вызове конструктора, потом при вызове map. Но такой вариант безальтернативно быстрее на хроме.
// reduce
arr.reduce((acc, val) => acc.concat(val), [])
// flatMap
arr.flatMap(x => x)
// flat
arr.flat()
// reduce push
arr.reduce((acc, val) => {
if (val) val.forEach(a => acc.push(a));
return acc;
}, [])
// forEach push
let acc = [];
arr.forEach(val => {
val && val.forEach(v => acc.push(v));
}), acc;
//concat spread
[].concat(...arr)
Конкатенация массивов на 1 уровень, поведение идентичное flat(1)
. Линк [7].
Иногда я не понимаю, почему разработчики движков оставили такой потенциал для оптимизации.
Бенчмарки ниже я перепроверял по нескольку раз, результат одинаковый и верный. Лиса действительно такая быстрая.
Сравнивать будем Array.prototype.forEach vs for...of vs for
. На код смотрите по линку [8].
Ради производительности, циклы for
, лучше переделать в forEach
, чтобы хром не отставал.
// text.includes()
url.includes('matchthis')
// text.test()
/matchthis/.test(url)
// text.match()
url.match(/matchthis/).length >= 0
// text.indexOf()
url.indexOf('matchthis') >= 0
// text.search()
url.search('matchthis') >= 0
Трюк с IndexOf
быстрее и на лисе, и на хроме. Используйте трюк с IndexOf
. Линк на бенчмарк [9].
Тестируем неявное преобразование, парсинг и вызов конструктора.
// implicit
var imp = + strNum
// parseFloat
var toStr = parseFloat(strNum)
//Number
var num = Number(strNum)
Линк на бенч [10].
Я перепроверял, это не ошибка. Неявный каст стринги в инт практически бесплатный у лисы. Линк на бенч [11].
Автор:
programmerguru
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mozilla-firefox/382289
Ссылки в тексте:
[1] Линк: https://www.measurethat.net/Benchmarks/Show/22097/0/1-var-vs-2-vars-vs-3-vars
[2] Линк: https://www.measurethat.net/Benchmarks/Show/22083/0/const-vs-let-vs-var-vs-sloppy
[3] Линк: https://www.measurethat.net/Benchmarks/Show/22099/0/no-type-coercion-switch-case-vs-bounce-pattern-vs-terna
[4] Линк: https://www.measurethat.net/Benchmarks/Show/22098/0/type-coercion-switch-case-vs-bounce-pattern-vs-ternary
[5] Линк: https://www.measurethat.net/Benchmarks/Show/22100/0/no-loop-switch-case-vs-bounce-pattern-vs-ternary
[6] Линк на бенч: https://www.measurethat.net/Benchmarks/Show/22086/0/array-initialization-for-for-push-fill-map
[7] Линк: https://www.measurethat.net/Benchmarks/Show/22085/0/flat-vs-flatmap-vs-reduce-vs-reduce-push-vs-foreach-pus
[8] по линку: https://www.measurethat.net/Benchmarks/Show/22095/0/side-effect-for-i-vs-for-of-vs-foreach-fix
[9] Линк на бенчмарк: https://www.measurethat.net/Benchmarks/Show/22090/0/includes-vs-test-vs-match-vs-indexof-vs-search-fix
[10] Линк на бенч: https://www.measurethat.net/Benchmarks/Show/21897/0/implicit-vs-parseint-vs-number-string-to-num
[11] Линк на бенч: https://www.measurethat.net/Benchmarks/Show/22092/0/implicit-vs-parsefloat-vs-number-string-to-num
[12] Источник: https://habr.com/ru/post/712386/?utm_source=habrahabr&utm_medium=rss&utm_campaign=712386
Нажмите здесь для печати.