Разные трюки я тестировал на 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 с нулями внутри. Линк.Здесь и далее в качестве единицы измерения на графиках будет указано кол-во выполнений кода в секунду, включая парсинг и компиляцию
Хром не заметил разницы, а вот лиса заметила. Применительно к лисе, у лишней переменной есть измеряемый оверхед.
Есть ли разница между var, let, const или их отсутствием?
Проверим. Используя разные биндинги — создадим POJO с переменной
е
. Потом добавим ему функцию о
и запустим эту функцию. Бенчмарк простой, но движущихся частей много. Линк.var g = { e: [] }
g.o = function(x) { g.e.push(...[1,2,3]) }
g.o()
Код выглядит так, отличаются только биндинги.
Результат неожиданный, но железно воспроизводимый.
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);
Вызов выглядит так. Линк.
▍ 2. В цикле с другим типом
for (let t = 0; 1e5 > t; t++) bounce("0"), bounce("2"), bounce("");
Тут мы покидываем строку вместо числа. В свитче и
if
блоках используется строгое равенство, поэтому свитч выходит только через default
, а if
’ы выходят только последний return
. Линк.▍ 3. Без цикла
bounce(0), bounce(2), bounce(6)
Просто три вызова подряд, никаких циклов. Линк.
Похоже, что после первоначальной компиляции лиса не пытается дальше оптимизировать цикл, как это делает хром.
Также лиса, похоже, не строит одно и то же 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. Линк на бенч.
Вариант с
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)
. Линк.Иногда я не понимаю, почему разработчики движков оставили такой потенциал для оптимизации.
Уничтожение хрома
Бенчмарки ниже я перепроверял по нескольку раз, результат одинаковый и верный. Лиса действительно такая быстрая.
▍ Итерация по массиву
Сравнивать будем
Array.prototype.forEach vs for...of vs for
. На код смотрите по линку.Ради производительности, циклы
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
. Линк на бенчмарк.Преобразование строки в число
Тестируем неявное преобразование, парсинг и вызов конструктора.
// implicit
var imp = + strNum
// parseFloat
var toStr = parseFloat(strNum)
//Number
var num = Number(strNum)
▍ Int
Линк на бенч.
▍ Float
Я перепроверял, это не ошибка. Неявный каст стринги в инт практически бесплатный у лисы. Линк на бенч.
Выводы
- Лисичка похорошела.
- JS сделан за неделю на коленке.
- Я не пишу на JS.
- Вы тоже прекращайте.
Играй в нашу новую игру прямо в Telegram!
Комментарии (64)
Cerberuser
24.01.2023 13:05+1Результат неожиданный, но железно воспроизводимый. var, быстрее.
На моём Chrome "железной воспроизводимости" не получилось - результат 1, результат 2, результат 3. Что-то с настройками, или просто тест такой, что в моей системе он заглушается шумом?
programmerguru Автор
24.01.2023 13:20Предполагаю, что да.
Попробуйте залочить все ядра на одну частоту, отключить C-State'ы и установить план питания на макс. производительность.nin-jin
24.01.2023 15:11+3Кто такие эти систейты и где они отключаются?
programmerguru Автор
24.01.2023 15:29+8Они с нами в одной комнате?
Они в BIOS'e, если у вас ноутбук или "офисная" материнка, то сделать не получится.lorc
24.01.2023 22:11+4cat 1 > /sys/devices/system/cpu/cpu*/cpuidle/state[1-3]/disable
не поможет разве?Ну и
scaling_governor
выставить вperformance
само собой
efkz
25.01.2023 12:09+6А насколько ваш тест применим к реальной жизни?
У юзеров ядра не залочены и C-State не отключены.
victor-homyakov
25.01.2023 02:08+12С настройками всё нормально. Если в бенчмарке для фронтенда, чтобы увидеть стабильную разницу, нужно играться с настройками железа - проблема не в настройках (99% пользователей этого не будут делать), а в самом бенчмарке, который пытается найти микроскопические отличия, легко съедаемые шумом и потому в большинстве случаев незаметные пользователю.
LyuMih
24.01.2023 13:45+14Статья опасная на мой взгляд. Новички в JS могут пойти в излишнюю оптимизацию вместо того, чтобы думать головой. Возьмут за основу раздел про if/else/switch или array.filter().map() и будут сувать куда не поподя эти правила "оптимизации". А ещё var быстрее let и const...
Выводы из серии:
Я не пишу на Java.
Вы тоже прекращайте.
ermouth
24.01.2023 14:18+8Статья опасная на мой взгляд
Так ведь любые знания опасны, мало ли как их будут использовать новички )
По-моему, статья норм, если промотивирует людей, которые никогда особо не задумывались о цене разных идиом, тестировать различные подходы в критичных по производительности местах.
Не всё, что в материале написано, будет работать в реальном коде прямо как на графиках – рантайм-оптимизация вообще очень тёмная и не всегда предсказуемая магия – но знать про масштабы этой магии под капотом точно не лишне.
А ещё var быстрее let и const
Это давняя история, с рождения. Есть, условно, базовая реализация var, очень давно и хорошо оптимизированная, и она поверх обмазана дополнительными ограничениями и механиками. Они не бесплатные.
shuhray
24.01.2023 15:56Лиса быстрее, но она теперь падает регулярно. Я не уходил с Лисы до последнего, пока не стала падать.
aborouhin
24.01.2023 17:54+16Лиса с сотнями вкладок (знаю, это не лучшая практика, но у меня отложенные на почитать потом страницы так и висят в виде вкладок, упорядоченных в папочки расширением Sidebery), 26 расширений, открыта практически круглосуточно. Судя по данным about:crashes, которые подтверждаются личными воспоминаниями, за 2022 год она вылетала 3 раза. ЧЯДНТ?
domix32
25.01.2023 11:56+1Честно говоря я удивлён, что люди ещё ловят краши без внешних форс мажоров. Мои "краши" обычно выглядит как kil -9 в сторону процесса или жёсткое отключение питания.
Хотя я уже давно не пользовался 32-битными версиями.
UMenyaNeudobnieVoprosiki
25.01.2023 12:05Спасибо, даже не знал про эту вкладку. До недавнего падала вполне себе успешно, особенно в DevTools и при разработке всяких тяжёлых штук. Вот не далече как вчера пытался попрофайлить один фриз - колом вставала вкладка и у FF, и у хрома, висит, висит, а потом "а чо-та ничо не работает, давайте эту вкладку просто убьём". Т.е. технически FF не падает, но и сайтег не работает) ООМ, скорее всего, тоже не каждый раз посчитает, как и ситуации аля "сожрало 100% проца, всё висит, пришлось ресетнуть". Маппинг на отладочные штуки собранные через webpack на мою память никогда нормально не работал, ну т.е. он работает до первого изменения, а потом новые маппинги не подтягиваются, дебаж как хочешь.
Ну и пришлось выкосить всё кроме vimium, instapaper.При этом переходить на хром всё равно не вариант, там бесит примерно всё, особенно отсутствие нормального Reader View
Artyom_Silchenko
24.01.2023 20:21+1Исходных данных мало. Пишу свой опыт: FF 109 x64 (последний ESR релиз, может другой номер), Win7 SP1 x64, 3 дополнения, более ста вкладак. FF падала раза 4. Скорее всего на каких то определенных сайтах "текут" вкладки. Сам пока не вычислил
joffer
24.01.2023 23:00+40 падений за последние, наверное, года 3. Всегда использую последнюю официальную версию, ОС Ubuntu 22, постоянно открыто 30 - 40 вкладок, куча закладок, куча дополнений интегрировано.
extensions
Mikola-BLR
25.01.2023 01:48+7За последние лет 7 довелось поработать на различных домашних / служебных компах - 3 ноутбука, 4 десктопа.
Везде не было никаких падений, кроме одного рабочего десктопа, который мне дали 6 лет назад. Вот прям регулярно валилась Лиса именно на нём, по несколько раз за день.
Залез в БИОС, поигрался с настройками ОЗУ - тут же прекратила падать. Вот как рукой сняло проблему и потом за ещё год работы на этой машине по 5 дней в неделю по 8 часов - ни единого падения.
Из чего я для себя сделал вывод, что проблема хардварная: из-за кривых настроек БИОСа память начинает давать ошибки, к которым Лиса очень чувствительна. Не зря на серверах используется ECC-память (error-correcting code memory, память с коррекцией ошибок).
Придя домой, решил воспроизвести проблему на своём домашнем десктопе. Залез в БИОС и разогнал память выше указанного дефолтного значения 3000 МГц до 3200 МГц. Винда запускается, вроде бы всё нормально работает, а Лиса начинает падать каждые несколько минут. Вернул частоту к привычным 3000 МГц - стабильно работает до сих пор.
Так что смотрите, что у вас с памятью в компе, не разогнана ли чрезмерно, не барахлит ли питание. Если есть в БИОСе возможность управлять разгоном, то снизьте частоту / добавьте напруги / увеличьте тайминги.
samsdemon
25.01.2023 09:56+1Может вашу проблему уже пофиксили) https://hacks.mozilla.org/2022/11/improving-firefox-stability-with-this-one-weird-trick/
0Bannon
25.01.2023 01:54А у меня на ноутбуке линукс минт и фаерфокс. И почему-то фаерфокс при прокрутке плиток видео на ютубе, когда они подгружаются начинает дико тормозить. Решил на оперу перейти.
nin-jin
24.01.2023 16:04+3Ради производительности, циклы
for
, лучше переделать вforEach
, чтобы хром не отставал.https://perf.js.hyoo.ru/#!bench=9w6t4a_4y41db
Хром
Лис
---
Трюк с
IndexOf
быстрее и на лисе, и на хроме. Используйте трюк сIndexOf
https://perf.js.hyoo.ru/#!bench=dn90ur_6qawpe
Хром
Лис
---
Я перепроверял, это не ошибка. Неявный каст стринги в инт практически бесплатный у лисы.
https://perf.js.hyoo.ru/#!bench=q4euna_ln9xk8
Хром
Лис
---
Я не пишу на JS.
Спасибо за полезные советы.
event1
24.01.2023 18:01+21Простите за глупый вопрос, а что нарисовано на графиках? Каковы единицы измерения?
programmerguru Автор
24.01.2023 18:22+2Количество выполнений кода в секунду, включая парсинг и компиляцию.
san-smith
24.01.2023 19:08+21Всю статью мучил тот же вопрос:)
Может добавите в начало статьи пояснение?venanen
25.01.2023 01:43+2Поддерживаю. Сначала думал, что это миллисекунды. Потом микросекунды. Думал, какой хром медленный однако, но когда там появились цифры с кучей нулей, я начал что-то подозревать
maeris
24.01.2023 19:05+2Результат неожиданный, но железно воспроизводимый. var, быстрее.
Вполне ожиданный: varу не нужно создавать скоуп, в котором будут объявлены переменные, потому что он уже есть. Поэтому, да, во вложенных циклах высокопроизводительного кода лучше всё-таки сделать var.
YuryB
24.01.2023 20:15+4микробенчмарки? :) в этом надо разбираться прежде чем такие тесты лепить. для начала я не вижу среднеквадратичной ошибки в результатах. вопрос прогрева, отключения турбобуста на процессоре и т.д. тоже актуально.
retry
25.01.2023 10:21+1Методика конечно не строгая, а по поводу турбобуста явно же автор написал.
>Чтобы результаты всегда были железно воспроизводимыми, я отключил все С-State’ы, ядра зафиксировал на 5 ГГц.
victor-homyakov
24.01.2023 22:38+7Есть ли у переменной оверхед?
Во-первых, у меня ни на Intel ни на M1 не воспроизводится - отличия результатов находятся в пределах естественного разброса результатов бенчмарка, и в разных запусках рандомно лидирует разный вариант. Для проверки можно в одном бенчмарке сделать несколько тестов с одним и тем же количеством локальных переменных - между ними тоже будут заметные отличия.
Во-вторых, если проанализировать результат автора (кстати, было бы не лишним указать погрешность измерений, которую показал бенчмарк):
один
var
и триmap
для массива на 65535 элементов занимают 1/345 секунды (345 ops/sec)три
var
и триmap
для массива на 65535 элементов занимают 1/319 секунды (319 ops/sec)
v + 3*m = 1/345 3*v + 3*m = 1/319
где
v
- длительность (оверхед) одногоvar
,m
- длительность одногоmap
массива из 65535 элементовПолучим v ≈ 118мс, m ≈ 927мс, то есть, утрируя, объявить девять лишних
var
- всё равно что обработать массив на 65 тысяч элементов? Объявление лишней промежуточной переменной не может быть настолько заметным. Кажется, этот бенчмарк измеряет что-то совсем другое.Есть ли разница между var, let, const или их отсутствием?
Во-первых, не могу воспроизвести результат на Intel и M1 ни на бенчмарке автора, ни на своём. Самым быстрым в Firefox оказывается рандомно то
const
то вообщеsloppy
, а самый медленный результат отстаёт от него всего на 7-8 процентов.Если посмотреть на код бенчмарка
var g = { e: [] } g.o = function(x) { g.e.push(...[1,2,3]) } g.o()
то можно увидеть, что он пытается измерять множество вещей одновременно: инициализацию переменной, создание объекта, создание функции, вызов функции, spread operator, garbage collector в рандомные моменты времени, и много чего ещё. То есть на одно создание переменной приходится три чтения и много других операций. Даже если автор прав и корректные результаты показывает только его бенчмарк, то результат применим только в ситуации, когда каждая переменная читается всего несколько раз и потом выбрасывается. И даже в такой ситуации замена
const
наvar
даст ускорение всего лишь во втором знаке после запятой - с 21463673 ops/sec до 21843333 ops/sec, то есть экономию в 0.81 наносекунды на одно выполнение вышеприведённого кода. Потребуются миллиарды итераций, чтобы суммарная разница достигла заметной глазу пользователя величины.Конечно, теоретически разница не может равна нулю:
let
в местах использования должен выполнять проверки на temporal dead zone и на область видимости, из-за этого он всегда немного медленнее, чемvar
const
должен выполнять проверки на область видимости и на temporal dead zone; как только константа будет проинициализирована, проверку на temporal dead zone можно больше не выполнять
Но в приведённом бенчмарке я вижу, что вся разница тонет в шумах.
В итоге ответ на вопрос "увижу ли я профит от использования var" сильно зависит от конкретного кода и от конкретной версии движка. Например, в Fastify поглядели на бенчмарки и позволили себе в большинстве мест заменить
var
наconst
иlet
- в свежих версиях Node.js разницы в скорости не видно.Поэтому на практике о такой мелочи можно не задумываться.
P.S. Кстати, раз уж упомянул Fastify - полезно посмотреть на пулл-реквесты с оптимизациями производительности раз два
sebres
25.01.2023 17:27+1Совершенно верно... Вообще очень странные тесты...
В конкретном случае -var vs let vs const vs sloppy
там по большей части вообще измеряется spreading array mutations & push (+ память/кэш всех уровней, + GC, + тому подобное), но никак не заявленное в названии теста.
Если переписать тест хотя бы без вызова push и spread-op (что справедливо ибо объявленнаяg
всё равно immutable), всё очень сильно изменится (и соответственно возможноvar
будет быстрее только лишьsloppy
, проигрывая иlet
иconst
):- g.o = function(x) { g.e.push(...[1,2,3]) } + g.o = function(x) { g.e.push }
"Возможно" - потому что оверхед собственно измерения и соответственно шумы и погрешности будут много выше той незначительной разницы (если она вообще есть в SpiderMonkey или V8 при компиляции в браузере).
<img src="боромир-мим.jpg"/>Нельзя просто взять и померить разницу в скоростиvar vs let vs const
в JS в браузере, просто потому что там нет подходящего быстрого инструмента (цикла) для измерений, так чтобы при компиляцииvar, let или const
результат не вырождался тупо в константное выражение, посчитанное на стадии компиляции цикла.
Т.е. даже что-то подобное нижеследующему вряд ли покажет ту разницу реально (и те +13ms или +173ms ниже тупо не являются какими-либо флуктуациями опять же из-за огромного overhead сверху):// 1e9 итераций, const vs var: (() => { const x = 1, X = {f: function() {return x}}; var v = 0; performance.mark('m1'); for (let i = 1e9; i > 0; i--) { v += X.f()+X.f()+X.f()+X.f()+X.f(); } performance.mark('m2'); })(); performance.measure('test', 'm1', 'm2') ► PerformanceMeasure {..., duration: 1057} (() => { var x = 1, X = {f: function() {return x}}; var v = 0; performance.mark('m1'); for (let i = 1e9; i > 0; i--) { v += X.f()+X.f()+X.f()+X.f()+X.f(); } performance.mark('m2'); })(); performance.measure('test', 'm1', 'm2') ► PerformanceMeasure {..., duration: 1070} // 1e10 итераций, const vs var: (() => { const x = 1, X = {f: function() {return x}}; var v = 0; performance.mark('m1'); for (let i = 1e10; i > 0; i--) { v += X.f()+X.f()+X.f()+X.f()+X.f(); } performance.mark('m2'); })(); performance.measure('test', 'm1', 'm2') ► PerformanceMeasure {..., duration: 13077.5} (() => { var x = 1, X = {f: function() {return x}}; var v = 0; performance.mark('m1'); for (let i = 1e10; i > 0; i--) { v += X.f()+X.f()+X.f()+X.f()+X.f(); } performance.mark('m2'); })(); performance.measure('test', 'm1', 'm2') ► PerformanceMeasure {..., duration: 13250.1}
victor-homyakov
24.01.2023 23:23+21Преобразование строки в число
Прямо классика
TL;DR: Бенчмарк показывает фантастические результаты в Firefox только потому, что оптимизатор понимает: на вход всегда поступает одна и та же строка, отсутствуют вызовы функций с заранее неизвестными сайд-эффектами, а результат преобразования нигде не используется, значит все вычисления можно выбросить.
Более правильный бенчмарк с минимальной попыткой подсунуть разные числа и хоть как-то использовать результат преобразования сразу возвращает с небес (1E+09 ops/sec) на землю.
eboyar
25.01.2023 01:56В чём смысл этой статьи? Сравнивать хром и лису только чтобы заключить "Я не пишу на JS и вам не советую"? Где нормальный вывод?
rutexd
25.01.2023 08:54-5Допустим. А что дальше? Лиса может быть и быстрее и лучше и даже лучше оптимизированее. Только ею пользоваться невозможно. Пишут свои веб стандарты которым на остальные все равно, ui тупит как в 2005 или хуже, по тестам на хабре же - сливает ещё больше данных чем хром.
AlexM2001
25.01.2023 10:20по тестам на хабре же - сливает ещё больше данных чем хром.
Можно ссылку на такие тесты?
rutexd
25.01.2023 17:25+1к сожалению, того теста о котором я говорю, я сейчас уже не найду, ибо дело было в пределах 21-22 годов. В попытках найти нашел другую, хоть и намного менее конкретную статью но не менее занимательную. https://habr.com/ru/company/brave/blog/551588/
Одна цифра в 2700 запросов и целый абзац с детальным списком отправляемой инфы уже говорит о "защищенности" и "приватности" "самого приватного и безопасного браузера". Нашел бы оригинальную статью о которой говорил - с радостью, отправил бы.
auresio
25.01.2023 14:05+2Пишут свои веб стандарты
Вы как то перепутали сторону, свои веб стандарты пишет гугл. А весь интернет радостно поддакивает. Вот только с адблоком прокол вышел.
ui тупит как в 2005 или хуже
Тут вообще без комментариев.
rutexd
25.01.2023 18:28-3Эти стандарты как минимум принимаются на широком уровне - оно и хорошо. Времена, когда один браузер умел одно, другой другое, третий ни того ни того а четвертый, извиняюсь, в песочнице жевал свои слюни - уже должны были давно пройти. Мы не в нулевых. Или хотя бы не при Медведеве. И гугл сделал очень многое для создания этой унификации. Но вместо этого у нас досих-пор 27 стандартов и половина из них легаси которая непонятно почему еще живет и орёт о уважении к себе.
Если вы занимались разработкой сайтов, не используя новомодных технологий вроде вебпака или прочей лабуды, которая за вас 8\10 проблем решает, то вы должны знать насколько лиса капризна. Отсутствующие кодеки видео банально, другие именования вещей вроде стилей или тех самых кодеков при ручном указывании, даже изменить полоску прокрутки - целое событие над которым надо посидеть что бы оно заработало. Свои стандарты, вроде добавления сайтами в нативное меню кнопок. Вопрос зачем и почему. А потом приходит некто и спрашивает "ой а почему у меня кнопочки нету" после лисы. Свои апи в js, часть которых либо во всем остальном мире депрекейтед либо вообще оффициально не поддерживается (какой нибудь XPConnect , например, если мне не подводит память он все еще поддерживается в лисе или mozGetUserMedia условный. И да, ничего против обратной совместимости я не имею но не настолько)
Если вы работали на средне-слабом \ старом железе в лисе, то вы знаете как безбожно она может тупить. Начиная от интерфейса который банально виснет от первого пука, заканчивая нагрузкой процессора при более десятка открытых вкладок. Не всегда и не во всех случаях но такое бывает время от времени. На хроме же - кроме потребления памяти на моей памяти ничего не было. Даже что бы заставить хром зависнуть - надо постараться. Хром еще с 2012-2014 когда я с ним впервые познакомился - летал. Для лисы же, для достижения подобной скорости и удобства, потребовался громкий релиз квантум (или как он там называется?). Однако на счет последнего я утерждать не берусь - слышал по осколкам. Как сейчас обстоят дела - врать не буду - не знаю - однако еще пару лет назад лично на моем опыте было в десяток раз больше проблем чем потенциальных плюсов.
Кстати, по этому скорее беглому тесту нежели статье, потребление лисы далеко не самое эталонное в сравнение с каким нибудь хромом, несмотря на данную статью: https://habr.com/ru/post/589923/
Если же не работали с сайтами или у вас никогда не было ничего древнего и бородатого старше чем на 5 лет - вам видимо просто очень повезло. К счастью или же нет.
Альтернатива нужна. Но учитывая текущие реалии и масштабы горя, это крайне трудоёмкий или даже непосильный без всемасштабного движа процесс. И лиса хоть и пытается быть в тренде, проигрывает не просто так и не просто из за бытия в тени большого брата гугла.
elvas7
25.01.2023 10:20-1Вы же в курсе, что firefox это не лиса?
https://facts.museum/3181#:~:text=Название браузера Mozilla Firefox не,малой панды в малой форме.mayorovp
25.01.2023 10:25У них даже на логотипе лиса...
elvas7
25.01.2023 11:10они сами не поняли, что имели ввиду - https://support.mozilla.org/en-US/questions/988854
но именно firefox - это не лиса, а красная панда
playermet
25.01.2023 11:30Логотип Firefox конечно все более минималистичен с каждой новой версией, но как минимум первые десять лет на нем отчетливо узнается слева лиса, а справа огонь. Причем там даже пигментация морды прорисована, а так же характерные щеки, острые длинные рыжие уши, тонкие рыжие лапы, длинная морда, острый нос, хвост без полос и т.д. и т.п. Т.е. полное совпадение с тем, как выглядит обычная лиса, и полная противоположность тому, как выглядит красная панда.
А если на логотипе лиса и огонь, то значит назван он в честь лисы и огня. Более того, он чисто исторически был сначала Phoenix, потом Firebird, а потом bird поменяли на fox, а fire всегда был центральной темой логотипа.
nin-jin
25.01.2023 21:12-1А вы в курсе, что морская свинка не имеет никакого отношения ни к морю, ни к свиньям?
Alexufo
26.01.2023 13:44-1Ну мы же не можем проверить обратного, потому что потерялся первоисточник, но это совсем не означает, что она не имеет отношения к морю и к свинье. Исторически может иметь совершенно.
WVitek
25.01.2023 12:22Сижу на Firefox ESR 32-bit. Памяти ест меньше, тормоза или нестабильности - исключительно редко.
Единственное, что "сломалось" на каком-то из обновлений, так это Web UI торрент-клиента transmission (кнопки и меню не работают, в Edge при этом норм).
rease
25.01.2023 12:45+2К сервису measurethat, и к тем тестам, которые на нем пишут, вопросы возникали уже не раз. И некоторые Ваши тесты не исключение.
Но тест "Инициализация массива", это просто уже яркий пример того, что нужно задумываться.
По вашему тесту получается что, вместо того, чтобы просто создать массив и пройтись по нему один раз циклом, я сначала вызову метод "fill()", который внутри пройдет по нему циклом и заполнит "undefined", потом вызову "map(initializer)", который под капотом создаст новый массив и снова пройдет циклом заполняя его результатами функции "initializer". И вот это вот все будет работать в хроме быстрее В ВОСЕМЬ РАЗ (998 / 127)? Серьезно?
На сколько мне известно, когда хром видит в коде стандартный цикл "
for(...) {...}
", он может оптимизировать код внутри блока так, как если бы это был код внутри функции, т.е. если нет сайд-эффектов, то он может полностью выпилить весь код в блоке. Но сам цикл он оставляет и будет его итерировать, т.к. это стандартная статическая языковая конструкция и она "невыпиливаемая". (Простой пример, написать "for(;true;);", и браузер успешно намертво зависнет).Когда вы тестируете "
new Array(times).fill().map(initializer)",
ваш массив никуда не возвращается и нигде больше не используется и для хрома, после оптимизации, является "мертвым", а далее вы вызываете встроенные в движок методы массива, который для хрома уже не более чем "мусор". И передаете функцию "initializer", которая не имеет сайд-эффектов и ее даже необязательно вызывать.Оптимизация)
На деле же, все это будет работать раза в 4 медленнее.
Добро пожаловать в реальный мир)))
const times = 65535 function initializer(val, z) { const i = z % 5 | 0 return (z % 3 | 0) == 0 ? i === 0 ? 'fizzbuzz' : 'fizz' : i === 0 ? 'buzz' : z } function test_for() { // Bench for let b1 console.time('bench for') for (let j = 0; j < 1000; j++) { b1 = new Array(times) for (let i = 0; i < times; i++) { b1[i] = initializer(b1[i], i) } } console.timeEnd('bench for') // console.log([b1]) return b1 } function test_map() { // Bench fill map let b2 console.time('bench map') for (let j = 0; j < 1000; j++) { b2 = new Array(times).fill().map(initializer) } console.timeEnd('bench map') // console.log([b2]) return b2 } setTimeout(test_for), setTimeout(test_map) setTimeout(test_for), setTimeout(test_map) setTimeout(test_for), setTimeout(test_map) setTimeout(test_for), setTimeout(test_map) /* bench for: 568.27294921875 ms bench map: 1711.642822265625 ms bench for: 594.904052734375 ms bench map: 1728.125732421875 ms bench for: 478.802978515625 ms bench map: 1684.43798828125 ms bench for: 474.054931640625 ms bench map: 1763.044189453125 ms */
.
kahi4
25.01.2023 13:23+2Поддерживаю.
Для теста:
▍ Итерация по массиву
Имеем результат на моем компьютере: for i x 153 ops/sec ±0.28% (63 runs sampled) в хроме и for i x 11,522 ops/sec ±0.51% (62 runs sampled) в firefox
Открываем консольку, запускаем
var array = new Array(65535).fill(0); var q = 0; var t1 = performance.now(); for (var repeat = 0; repeat < 100; repeat++) { for (var i = 0; i < array.length; i++) { q = array[i] * array[i]; } } console.log(performance.now() - t1);
Ожидания:
Хром: ~800ms (100 iterations * 153 it/sec)
Firefox: ~ 1ms (100 iterations * 11 522 it / sec)
Реальность:
Хром: 14.5ms
Firefox: 3799ms
(Изначальный тест был с сотней повторов, но фаирфокс так вообще не умеет).
Вывод: я знаю что запустить такой тест в консоли бессмысленно, консоль может выполняться без jit, быть непрогретой и тому подобное, но результаты консоли слишком сильно расходятся с результата бенчмарка, причем для хрома в 60 раз в пользу консоли, для FF в несколько тысяч раз в сторону бенчмарка. Короче: весь бенчмарк показывает близкие к случайным числам, поэтому никакие выводы на его основе делать я бы тоже не стал.
rease
25.01.2023 19:27+1Да согласен, потому что бенчмарки так не пишутся.
Но проблема в том, что люди прочитав статью, могут это все принять за чистую монету, и потом пойдут все свои циклы "for" на "map" или "forEach" переписывать, думая что у них после этого перфоманс в 8 раз взлетит. Хотя в действительности так не будет.
Действительность:
const arr = new Array(65536).fill(0) function callback(v, k, a) { a[k] = v + k } function test_for() { console.time('bench for') for (let j = 0; j < 1000; j++) { for (let i = 0; i < arr.length; i++) callback(arr[i], i, arr) } console.timeEnd('bench for') } function test_forEach() { console.time('bench forEach') for (let j = 0; j < 1000; j++) { arr.forEach(callback) } console.timeEnd('bench forEach') } setTimeout(test_for), setTimeout(test_forEach) setTimeout(test_for), setTimeout(test_forEach) setTimeout(test_for), setTimeout(test_forEach) setTimeout(test_for), setTimeout(test_forEach) setTimeout(test_for), setTimeout(test_forEach) setTimeout(test_for), setTimeout(test_forEach) setTimeout(test_for), setTimeout(test_forEach) setTimeout(test_for), setTimeout(test_forEach) setTimeout(() => { console.log(arr) }, 100) // РЕЗУЛЬТАТ: /* bench for: 478.824951171875 ms bench forEach: 973.968994140625 ms bench for: 471.277099609375 ms bench forEach: 934.132080078125 ms bench for: 124.044921875 ms bench forEach: 924.15087890625 ms bench for: 128.7451171875 ms bench forEach: 929.00927734375 ms bench for: 123.68798828125 ms bench forEach: 919.563232421875 ms bench for: 123.55908203125 ms bench forEach: 150.320068359375 ms bench for: 123.626953125 ms bench forEach: 148.734130859375 ms bench for: 124.884033203125 ms bench forEach: 148.5390625 ms */
YuryScript
25.01.2023 19:51+4Такие бенчмарки не то что бесполезные, даже вредные. Лучше бы скрыть статью и забыть о ней, меньше вреда будет.
ElvenSailor
26.01.2023 16:17+1ыбло бы неплохо привести единицы измерения на графиках, и комментарий вроде "меньше - лучше" и "больше - лучше", а то неочевидно, кто кого уделал.
Aquahawk
Не везде согласен с методологией, но сейчас ну совсем нет времени погружаться в детали, однако ещё в 2017 году я рассказывал (https://www.youtube.com/watch?v=I1LpqbzZmLM) почему таргечусь в ES3. Тогда, ради геттеров и сеттеров я быстро поднялся на ES5, и всё ещё моя рекомендация использовать все плюшки через Typescript и таргетиться в es5.
Aquahawk
Господа минусующее, вот без обид, но может кто-нибудь привести такой пример кода на ESNext который будучи странспилен в таргет ES5 будет медленнее? До сих пор я не видел ни одного такого примера. Почему если новая модная штука не даёт выигрыша в производительности стоит посылать её клиенту? Развитие языка это хорошо и правильно, конструкции новые многие удобные, но почему не оттранспилить их в более быстрое представление?
В Typescript в угоду скорости используют вообще локальные переменные в скоупе, чтобы не обращаться к свойствам объектов, огребая миллион проблем, получая вот такой замечательный файл на 2.7 мегабайта кода и 2.5 тысячи функций в одном файле https://github.com/microsoft/TypeScript/blob/main/src/compiler/checker.ts
И они не могут от этого уйти по причине производительности компилятора, я делал автоматизированный рефактор этого кода https://github.com/microsoft/TypeScript/issues/17861 и ребята гоняли тесты, производительность просела. С тех времён в тайпскрипте выразительные средства улучшились, и такой рефактор можно сделать красивее. Но они продолжают платить часами, днями и уже вероятно годами своих разработчиков за комфорт пользователя этого компилятора.
Я не понимаю программистов которые в угоду моде заставляют расплачиваться пользователя, а потом тут появляются треды на 1000 комментов что софт дерьмо. Да он дерьмо ели его дерьмово делать.
nin-jin
https://perf.js.hyoo.ru/#!bench=71gc0c_i1yal9
Aquahawk
Чёт я ничерта не понял как этот тест работает, там inc никто не дёргает, вы проверяете скорость конструирования объектов? Дальнейшее рассмотрю из этого предположения. По коду у меня два вопроса по существу, зачем вы явно написали extends Object, это же бессмысленная ересь, и второе, синтаксис объявления полей который вы применили значительно замедляет этот странный бенчмарк.
Я добавил к вашему бенчмарку немного дополнительных тестов, применив транспилер Typescript, а вы, похоже, применяли babel (тут могу ошибаться, но он любит через defineProperty объявлять, и я абсолютно не понимаю зачем это делать)
https://perf.js.hyoo.ru/#!bench=ww1cau_g81ws7
Выводы которые я вижу:
Бессмысленное наследование Object очень портит результат в всех случаях.
Объявления поля класса современным синтаксисом просаживает производительность вдвое
Таки да, в 2023 году мы видим что наконец обычное объявление Class стало таки быстрее. Но поле всё портит.
И тут интересно что когда я в 2017 году таки перешёл на ES5 (2009) прошло 8 лет с принятия стандарта, сегодня в 2023 прошло 7 лет с момента принятия es6(2015) в котором появились классы. И вот, наконец они быстры. Что интересно ни https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields ни https://github.com/tc39/proposal-class-fields не говорят в какой стандарт попали эти фичи. Но очередной раз я вижу как новый функционал в стандарте, даже спустя несколько лет после принятия тормозит. Должно пройти лет 7-10 чтобы он стал быстрым, и то не всегда.
Таким образом я признаю, что действительно есть конструкция из ES6 быстрее ES5, и возможно действительно сегодня уже стоит таргетиться в ES6. Но не свежее. И дело не только в тормозах, но и в том что в мире ещё осталось значительное количество клиентов на 32bit windows 7 которые сидят на старом хроме. Зависит от масштаба вашего бизнеса, но если у вас миллионы пользователей и монетизация ненулевая то выясняется что просто саппорт старых браузеров, достигаемой одной строчкой в виде таргета компилятора приносит существенное количество денег. И в идеале можно по userAgent отдать два разных билда, но на такое изврат даже я не готов.
P.S. а ещё что-то сломалось, вот тут https://perf.js.hyoo.ru/#!bench=twmjin_o4c5nl
victor-homyakov
Не всё так страшно. Ну OK, создание класса с полем стало занимать микросекунду вместо 600 наносекунд. Обратите внимание - класса, а не экземпляра класса. Это действие, которое обычно происходит только один раз в самом начале выполнения приложения (и поэтому надо смотреть на холодный замер).
Сколько классов может быть в современном жирном приложении, загружаемом в браузер? Если взять с потолка число в тысячу классов - с новым синтаксисом потребуется целая миллисекунда вместо 600 микросекунд. Даже если на старых компьютерах скорость выполнения в десятки раз медленнее - разница в скорости всё равно будет в десятках миллисекунд. Думаю, эта разница маловата, чтобы оправдать отказ от нового синтаксиса.
Интереснее было бы побенчмаркать создание экземпляров этих разных классов и вызов методов. Вы могли бы это сделать?
Aquahawk
К сожалению снова погружаться в бенчмаркинг я не готов потому что нужно очень тщательно вновь разбираться в методологии предложенного инструмента и гонять множество тестов, это займёт несколько дней, у меня их нет. Тогда в 2017 на подготовку доклада ушло около 60 рабочих часов при том что я уже знал примерно что показывать. А сейчас я вообще в другом технологическом стеке и иногда вытираю слезу вспоминая простой и понятный мир js и превосходный typescript
nin-jin
Вы и сами легко можете это сделать: https://perf.js.hyoo.ru/#!bench=ovgci8_vzub6j
CoolCmd
kangax говорит, что поля попали в стандарт 2022, хотя в браузерах есть в каком-то виде уже несколько лет, возможно отключенные по умолчанию. я проверил, в стандарте 2021 полей еще нет.
Aquahawk
Ну вот лет через 7 может бцдет нормально, в 2030 обсудим :)
victor-homyakov
Да, есть такая проблема. Многое новые языковые фичи сначала были реализованы как сахар поверх существующего языка, то есть буквально в коде на JS или вообще на специальном языке Torque, который движок выполнял "под капотом". Только потом, постепенно, самый часто используемый код оптимизировался, переводился на C++. Но успел образоваться замкнутый круг: разработчики не использовали условный bind, потому что рукописный полифилл заметно быстрее, а браузеры не оптимизировали bind, потому что его никто не использует.
Aquahawk
да, согласен что происходит именно это. Вопрос, зачем тогда тащить этит функционал в сам js, а не сосредоточиться только на его скорости и функционал который действительно даёт новые возможности типа тех же символов. Я за подход когда можно всё затакивать в typescrip или сделать диалект поверх babel, а сам низкойровневый js не раздувать.
morijndael
Или вообще подвинуть JS, и приделать, наконец к WebAssembly доступ к Web API.
После уже можно выдохнуть с облегчением и переписать все на раст /шА то получается комическая ситуация. Байткод, созданный для браузера может рулить полноценной операционной системой (см. WASI), но рулить браузером — нини, только через прослойки на JS и постоянную де/сериализацию всего туда-обратно
nin-jin
Большинство реальных классов от кого-нибудь наследуются.
Отладчик в Хроме показывает содержимое Symbol.toStringTag только для экзмепляров унаследованного класса.
Просто настройте TypeScript для совместимости с ESNext.
Что сломалось?
Vadem
Не понимаю почему вас минусуют. Тем более без аргументов.