- PVSM.RU - https://www.pvsm.ru -

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 1

Картинка, конечно, стронгли анрилейтед

Разные трюки я тестировал на 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].

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 2

Хром не заметил разницы, а вот лиса заметила. Применительно к лисе, у лишней переменной есть измеряемый оверхед.

Есть ли разница между var, let, const или их отсутствием?

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 3

Проверим. Используя разные биндинги — создадим POJO с переменной е. Потом добавим ему функцию о и запустим эту функцию. Бенчмарк простой, но движущихся частей много. Линк [2].

var g = { e: [] }
g.o = function(x) { g.e.push(...[1,2,3]) }
g.o()

Код выглядит так, отличаются только биндинги.

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 4

Результат неожиданный, но железно воспроизводимый. var, быстрее.

Bounce pattern, Switch case, длинная тернарка

Если обе конструкции логически одинаковые, они должны строить одно и то же синтаксическое дерево, верно? Давайте проверим.

// 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" : "";
}

Вот так выглядит код. Для всех вариантов он одинаков, отличаются только вызовы.

▍ 1. Вызов в цикле

for (let t = 0; 1e5 > t; t++) bounce(0), bounce(2), bounce(6);

Вызов выглядит так. Линк [3].

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 5

▍ 2. В цикле с другим типом

for (let t = 0; 1e5 > t; t++) bounce("0"), bounce("2"), bounce("");

Тут мы покидываем строку вместо числа. В свитче и if блоках используется строгое равенство, поэтому свитч выходит только через default, а if’ы выходят только последний return. Линк [4].

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 6

▍ 3. Без цикла

bounce(0), bounce(2), bounce(6)

Просто три вызова подряд, никаких циклов. Линк [5].

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 7

Похоже, что после первоначальной компиляции лиса не пытается дальше оптимизировать цикл, как это делает хром.

Также лиса, похоже, не строит одно и то же 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].

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 8

Вариант с fill map создаёт два массива, сначала при вызове конструктора, потом при вызове map. Но такой вариант безальтернативно быстрее на хроме.

Конкатенация массивов

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 9

// 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].

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 10

Иногда я не понимаю, почему разработчики движков оставили такой потенциал для оптимизации.

Уничтожение хрома

Бенчмарки ниже я перепроверял по нескольку раз, результат одинаковый и верный. Лиса действительно такая быстрая.

▍ Итерация по массиву

Сравнивать будем Array.prototype.forEach vs for...of vs for. На код смотрите по линку [8].

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 11

Ради производительности, циклы 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
Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 12

Трюк с IndexOf быстрее и на лисе, и на хроме. Используйте трюк с IndexOf. Линк на бенчмарк [9].

Преобразование строки в число

Тестируем неявное преобразование, парсинг и вызов конструктора.

// implicit
var imp = + strNum

// parseFloat
var toStr = parseFloat(strNum)

//Number
var num = Number(strNum)

▍ Int

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 13

Линк на бенч [10].

▍ Float

Js, трюки, наблюдения, бенчмарки и как Лиса уничтожает Хром. Я протестировал всё, что вам было лень - 14

Я перепроверял, это не ошибка. Неявный каст стринги в инт практически бесплатный у лисы. Линк на бенч [11].

Выводы

  1. Лисичка похорошела.
  2. JS сделан за неделю на коленке.
  3. Я не пишу на JS.
  4. Вы тоже прекращайте.

Автор:
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