Found 123 bookmarks
Custom sorting
Perflink
Perflink

Хороший сайт для создания простых перформанс бенчмарков на js. Не уверен, что это прям самый лучший сервис для бенчмарков, но он достаточно удобный, и если вам не нужна супер большая точность - то выглядит гуд.

Приятный интерфейс и возможность делиться ссылкой на конкретный бенчмарк без авторизации.

·perf.link·
Perflink
Retries – An interactive study of common retry methods
Retries – An interactive study of common retry methods

Очень крутая и интерактивная статья, наглядно объясняющая как работают разные retry стратегии.

В рамках статьи объяснения про то, зачем нужны retry и почему наивная реализация "повторить запрос через секунду" сопровождаются интерактивными примерами: в них визуально моделируется отправка запросов клиентами на сервера, а вы, как читатель, можете контролировать rate ошибок сервера, количество серверов и прочие параметры.

Некоторые примеры можно использовать как эталон для обучения. Мне очень сильно понравился кейс, где при переключении рейта ошибок на 100% сервера складываются, но при включении обратно в 0% они не поднимаются - клиенты отправляют слишком много запросов за раз, вновь поднятые инстансы не успевают их обработать. Но там можно добавлять новые сервера и автор ставит вопрос "как думаете, сколько нужно дополнительных серверов, чтоб система снова начала работать?", что прямо таки мотивирует сначала подумать, а потом и испытать свою догадку

·encore.dev·
Retries – An interactive study of common retry methods
How we optimized package imports in Next.js – Vercel
How we optimized package imports in Next.js – Vercel

Статья от Vercel про вред от паттерна barrel file. В статье vercel рассказывают, как они оптимизировали импорты в next.js чтобы избежать обработки таких файлов и как ускорили сборку и старт приложения на 30-40%

barrel-file - это когда, например, в корне библиотеки лежит index.js, который ре-экспортит кучу сущностей из других вложенных файлов. Проблема в том, что не смотря на то, что в коде используется всего лишь 1 ре-экспортируемая сущность, бандлерам и некоторым тулам, например jest, приходится сначала обработать все файлы. Что снижает скорость работы инструмента

Каноничный пример

export { default as module1 } from './module1'; export { default as module2 } from './module2'; export { default as module3 } from './module3';

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

Команда next.js ввела новую опцию в конфиг: optimizePackageImports, которая заставляет фреймворк просканировать barrel-file и в рамках сборки поменять импорты на более точечные. Это изменение позволило ускорить сборку на 15-70%. Например, импорт @material-ui/icons ускорился с 10 секунд до 2.9 секунды.

Также в статье указана ссылка на eslint-плагин, который умеет оптимизировать импорты barrel-файлов

·vercel.com·
How we optimized package imports in Next.js – Vercel
Speeding up the JavaScript ecosystem - The barrel file debacle
Speeding up the JavaScript ecosystem - The barrel file debacle

Новая статья про ускорение JS экосистемы. На этот раз автор не разбирает какой-то конкретный кейс, а в целом рассказывает об одном паттерне, который замедляет исполнение кода.

Паттерн сегодняшней статьи - это index-файлы с ре-экспортами. В сообществе есть консенсус большинства, что у библиотек или модулей должен быть index.js, который содержит все публичное API, и дальше лезть никому не нужно. А если вы используете не все из этого API, то tree-shaking вырежет все лишнее.

В целом, это так и есть, но не всегда. Tree-shaking актуален только для кода, который был обработан бандлером. Если же код не обрабатывается бандлером, то рантайм вынужден все import'ы обработать, что увеличивает время выполнения кода.

Например, если код не бандлить и просто поставлять модулями в браузер, то браузеру придется обработать все дерево зависимостей index.js файла, даже если вам нужна одна простая утилка.

Также это проблема, с которой я сталкиваюсь при написании тестов в jest. Код импортирует 1 утилку, но она импортируется из index.js файла, поэтому jest тратит 5-10 секунд на резолв всех зависимостей, и 100мс на запуск самих тестов.

При этом index.js файлы - это не анти-паттерн. Да, у него есть минусы, но они проявляются только если они используются в окружениях, где код запускается без бандлинга и если таких файлов неприлично много. Если index.js используются к месту или в проекте, который не живет без бандлинга - то такие файлы не будут создавать негативный эффект.

·marvinh.dev·
Speeding up the JavaScript ecosystem - The barrel file debacle
Diving into Engineering Metrics
Diving into Engineering Metrics

Обзор на разные метрики для оценки продуктивности команды разработки.

В статье обсуждается зачем нужны такие метрики и какие подходы есть в индустрии для сбора этих метрик

Зачем нам нужно замерять команды разработки:

  1. Метрики позволяют понимать производительность
  2. Замеряя метрики, команды могут видеть свое развитие
  3. Метрики могут быть использованы для принятия решений
  4. Метрики позволяют убедиться, что разработчики следуют целям бизнеса
  5. Устанавливать цели
  6. Позволяют обсуждать прогресс проекта со стейкхолдерами
  7. Улучшение метрик положительно влияет на мораль команды
  8. Метрики позволяют увидеть, что ресурсы используют неээфективно
  9. Метрики позволяют убедиться в достаточном качестве продукта

Но как правильно померить команду разработки? Это нетривиальная задача и подходы к решению этой задачи менялись с течением времени

Когда то давно, достаточно было замерять строчки кода (например, в книжке "мифический человекомесяц" часто встречаются кейсы, где проекты замеряются по строчкам кода).

Затем перешли на измерение velocity и cycle time. Velocity замеряет объем поставляемых в итерацию задач. Cycle time - время от "взяли задачу в проработкуработу" до "поставили ценность клиенту". Эти метрики уже намного ближе к бизнесу, чем строчки кода. Они проверяют, насколько команда гибкая (насколько быстро она проверяет гипотезы)

Позже, DevOps движение принесло DORA (DevOps Research and Assessment) - 4 ключевых метрики, по которым можно понять эффективность команды:

  1. Lead Time: сколько времени проходит от появления идеи до получения её клиентами
  2. Deployment Frequency: как часто команда поставляет изменения (деплоит в прод)
  3. Change Failure Rate: как часто изменениям нужны правки или переделки
  4. Mean Time To Recovery: как быстро мы можем исправить проблему, если она появилась

Эти метрики направлены на то, чтобы команды могли поставлять изменения как можно чаще с как можно большим качеством. Метрики DORA основаны на эмпирических данных кучи команд. Эти данные были собраны и затем выделены средние, лучшие и худшие результаты. Вы всегда можете загуглить текущий отчет DORA и сравнить свои показатели метрик с табличками оттуда и сделать выводы, что вам следует развивать.

Следующий набор метрик - SPACE.

  1. Satisfaction
  2. Performance
  3. Activity
  4. Communication and collaboration
  5. Efficiency and flow

Тут уже делается акцент на уровне счастья разработчика и на эффективности процессов. Но при этом, в статье (я не понял это прям в SPACE предлагается или в статье так интерпретировали этот набор метрик) предлагается замерять скорость и эффективность Code Review, что, на мой взгляд, не очень хорошо

В этом году появился DevEx, который предлагает замерять циклы обратной связи, когнитивную нагрузку и возможность для потоковой работы в разрезе персональной работы, системы и KPI

В общем, оценивать эффективность разработки очень сложно и есть разные подходы, которые можно комбинировать.

·hybridhacker.email·
Diving into Engineering Metrics
Honey, I shrunk the npm package
Honey, I shrunk the npm package

Статья рассматривает 3 различных стандарта для сжатия файлов: gzip, brotli (решение от Google) и ZStandard (решение от Facebook). Рассматриваются эти стандарты с точки зрения распаковки npm-пакетов. В целом, неплохой обзор на разные инструменты для сжатия контента

В рамках сравнения получились следующие выводы: ZStandard эффективнее сжимает чем gzip и быстрее распаковывается. Brotli же сжимает еще эффективнее, но не так быстр в запаковке и распаковке контента.

Но при этом для сжатия npm пакетов используется gzip. Почему так, если другие форматы эффективнее? Все дело в обратной совместимости. Brotli поддерживается нативно с npm 10 (если я правильно понял статью), а значит, пока есть клиенты с более мелкими версиями npm - включать brotli как решение по умолчанию - нельзя. Так что к 2027 году можно будет переходить на brotli по-умолчанию. А поддержки ZStandard в npm пока еще нет, поэтому вряд ли стоит ожидать его применения

PS: отдельно порадовала отсылка в названии статьи к фильму "Дорогая, я уменьшил детей", который был у меня в детстве на кассете :)

·jamiemagee.co.uk·
Honey, I shrunk the npm package
The Uphill Battle of Memoization
The Uphill Battle of Memoization

Есть статьи Дэна Абрамова и Кент Си Доддса, объясняющие, как правильно композировать компоненты в React, чтобы не требовалось использование React.memo. Но в чем проблема React.memo?

Статья разбирает, почему использование React.memo приводит к усложнению кода. Если коротко, то React.memo использует для сравнения Object.is, что влечет за собой требование прокидывания стабильных ссылок на объекты и функции в пропсы. А это, в свою очередь, требует заворачивания в useMemo всех объектов, вместо простого их прокидывания, даже если они не изменяются.

Например, в коде <ExpensiveTree style={{ backgroundColor: 'blue' }} /> при каждом рендере style будет новый, поэтому, чтобы не вызывать ререндер, нужно либо вынести style в константы за компонент (что не всегда возможно), либо завернуть объект в useMemo

Также, если компонент принимает children, то JSX разметка тоже не является стабильной ссылкой, из-за чего при каждом рендере children будет отличаться

Поэтому, вместо использования мемоизации следует рассматривать другие альтернативы: правильная композиция компонентов или стейт-менеджеры

·tkdodo.eu·
The Uphill Battle of Memoization
Хороший ретрай, плохой ретрай, или История одного падения
Хороший ретрай, плохой ретрай, или История одного падения

Хорошая статья, объясняющая на простых примерах практики для взаимодействия с сервером, который отдает ошибки. Наверняка, вы сталкивались с ситуацией, когда сервер иногда отвечает ошибкой, но если повторить запрос - то ответит корректно. Решение на поверхности - просто влепить retry. Что может пойти не так?

Статья как раз и объясняет что может пойти не так. Если коротко, то retry может привести к тому, что нагрузка на сервер вырастет и он перестанет справляться с обработкой запросов. После этого количество ошибок возрастет, количество retry увеличится, что приведет к еще более печальным результатам. В статье рассказывается как правильно делать обращения к нестабильному серверу

Первая практика: retry. Если все клиенты будут использовать retry с одинаковым таймаутом между запросами, то если серверу станет плохо - ретраи его добьют. Чтобы этого не случилось, используют exponential backoff retry - это когда ретраи делаются не через фиксированные отрезки времени, а эти отрезки увеличиваются. Грубо говоря, если сервер не смог ожить за первые 2 быстрых ретрая, то вряд ли он оживет в ближайшее время, поэтому есть смысл увеличивать с каждой попыткой интервал между попытками.

Если все клиенты будут ретраить с одинаковыми таймаутами, то нагрузка на сервер будет происходить волнами. Чтобы это устранить, добавляют jitter - случайную задержку к интервалам между ретраями.

Но в целом, при долгом отказе сервера, даже "умные" ретраи не спасают - они лишь откладывают пик нагрузки. Поэтому применяют и другие практики: Retry circuit breaker и Retry budget.

Практика Retry circuit breaker говорит о том, что ретраи допускается делать если количество ошибок от сервера не превышает порог. Если превышает - мы считаем что сервис немножко нездоров и долбить его ретраями - значит делать хуже

Практика Retry budget говорит о том, что ретраев не должно быть больше определенного порога от количества успешных запросов. Например, не больше 10% от запросов.

Но и это еще не все. Еще существует практика deadline propagation. Это когда клиент в запросе передает значение таймаута и сервер, получая запрос на обработку из очереди обработки, может принять решение - есть ли смысл его обрабатывать. Или прекратить обрабатывать запрос, ответ на который клиент уже не ждет.

В общем, крутая статья с хорошими примерами и графиками про основные паттерны борьбы с нагрузкой на нестабильный сервер

·habr.com·
Хороший ретрай, плохой ретрай, или История одного падения
Your Cache Headers Could Probably be More Aggressive
Your Cache Headers Could Probably be More Aggressive

Короткая статья про кеширующие хедеры. При настройке заголовков ответа на сервере или CDN можно указать правила кеширования. Если их правильно настроить, это сильно поможет и пользователю и приложению.

Автор разбирает 2 простых сценария: кеширование до изменения и вечное кеширование.

Если у вас есть ресурс, который иногда меняется, то имеет смысл выставить следующее значение заголовка Cache-Control: public, max-age=0, must-revalidate. В этом случае браузер при повторном указании ресурса будет спрашивать у веб-сервера, изменился ли указанный ресурс или нет. И если не изменился - сервер ответит 304 кодом, что означает, что браузер может взять контент из кеша. В этом случае выигрывают все - пользователь не скачивает лишний трафик, не ожидает загрузки. Сервер же не тратит свои ресурсы на раздачу файла

Если у вас есть ресурс, который никогда не меняется (например, JS-статика с хешом в имени), то в заголовок можно передать public, max-age=31560000, immutable.

public означает что ресурсы может быть закеширован как на устройстве так и на посредниках max-age=31560000 указывает, что ресурс кешируется на 1 год immutable означает, что браузер не должен перезапрашивать ресурс, если он уже есть в кеше.

В целом эти 2 совета могут быть верны для многих случаев, но в реальности могут быть случаи между двумя этими крайностями. Разработчику следует подумать о кешировании своих ресурсах т.к. правильная стратегия кеширования является вин-вин ситуацией: и пользователь и сервер выигрывают от правильно настроенного кеширования

·macarthur.me·
Your Cache Headers Could Probably be More Aggressive
On React Suspense’s throttling
On React Suspense’s throttling

Статья объясняет проблему тротлинга рендера при вложенных Suspense. Не уверен что это корректно называть тротлингом, но сама проблема интересна.

Представьте, что у вас есть вложенные Suspense. React работает так, что если первый Suspense ушел в ожидание раньше, чем рендер дошел до вложенного Suspense, то как следствие когда promise, подвесивший первый Suspense выйдет из ожидания, рендер дойдет до второго Suspense и мы бы ожидали увидеть контент первого Suspense и fallback у второго. Но на практике наблюдается некоторое время, когда мы видим состояние fallback первого Suspense. Так происходит потому что React умный и не комитит изменения Virtual DOM сразу в DOM, а ждет некоторое время. Из-за чего появляется задержка, когда мы бы могли уже показать пользователю контент, но этого не происходит.

В принципе, ничего криминального React не делает, но факт интересный

·andreigatej.dev·
On React Suspense’s throttling
Как показать миллион зданий на карте — и не сломать браузер
Как показать миллион зданий на карте — и не сломать браузер

Статья от 2ГИС про то, как разные агрегации по миллионам объектам на карте. 2ГИС часто радует своими статьями с интересными высоконагруженными кейсами, и эта статья - не исключение, хотя коротковата

В статье рассказывается, как можно обрабатывать огромные массивы данных в браузере. Если коротко, то решение 2ГИС в использовании следующих инструментов: WebWorkers, передача данных в бинарном виде, обработка на GPU

·habr.com·
Как показать миллион зданий на карте — и не сломать браузер
How we reduced the size of our JavaScript bundles by 33%
How we reduced the size of our JavaScript bundles by 33%

Статья от команды Dropbox о том, как они сменили бандлер и уменьшили размер JS статики на треть.

Dropbox - крупная компания со своими сложными вызовами и поэтому, в 2014 году они сделали свой бандлер и свою технологию Dropbox Web Server (DWS). DWS позволяет делать модульный веб - страница состоит из независимых страничных блоков (pagelet), которые разрабатываются и обрабатываются независимо друг от друга.

Кастомный бандлер сильно отстал от лидеров рынка и поэтому было принято решение переезжать на современные решения. Самые крупные проблемы предыдущей архитектуры:

  1. Дублирующийся код. Т.к. pagelet-ы независимы друг от друга, то если у них есть общий код, то они его встраивают в себя, вместо того чтобы переиспользовать
  2. Отсутствие автоматического код-сплиттинга. Бандлер поддерживал его, но только через ручной конфиг на 6000 строк
  3. Отсутствие три-шейкинга

Все эти проблемы давно решены текущими сборщиками, но не были решены в самописном решении. Dropbox переехал на использование rollup, но при этом миграция на него была плавная: сначала только dev сборка, затем только для сотрудников, затем раскатка на пользователей.

Во время плавной миграции были следующие трудности:

  1. Иногда rollup падал из-за высокого потребления памяти в CI
  2. Сложно поддерживать одновременно 2 бандлера
  3. Появились баги из-за слишком агрессивного три-шейкинга у Rollup
  4. Rollup включал strict mode для кода, что ломало некоторые зависимости

Как результат:

  1. Удалось уменьшить JS статику на 33%
  2. Количество JS файлов уменьшилось на 15%
  3. Улучшился TTVC (time to visual complete). Я так понял это одна из основных метрик, за которыми следит dropbox
·dropbox.tech·
How we reduced the size of our JavaScript bundles by 33%
Speeding up V8 heap snapshots · V8
Speeding up V8 heap snapshots · V8

История про оптимизацию перформанса генератора снапшота памяти в V8.

Инженеры bloomberg пытались поймать утечку памяти в своем nodejs приложении, но снапшот памяти на 500МБ снимался полчаса - непозволительно долго. Инженеры запрофилировали работу генератора снапшота и нашли 2 проблемы, решив которые они смогли ускорить генерацию снапшота на 100МБ памяти с 10 минут до 6 секунд (ускорение в 100 раз)

Первая найденная проблема - это алгоритм хеширования строк. V8 сохраняет все строки в отдельной оптимизированной хешмапе. Для чисел, представленных как строки, используется отдельный алгоритм для вычисления ключа для хранения в хешмапе. Этот алгоритм приводил к тому, что часто возникали колизии хешей, из-за чего операции с хешмапой замедлялись. Автор поправил алгоритм, добавив небольшой побитовый сдвиг.

Вторая найденная проблема - для каждой функции хранится информация о строке и колонке, где она начинается. Однако, v8 обычно сохраняет только количество предшествующих функции символов в файле и рассчитывает строку и колонку только тогда, когда это нужно (подсчитывая, по сути, символы переноса строки). Т.к. этот расчет - дорогой, то v8 хранит эти данные в кеше. Однако, при сборе снапшота этот кеш не используется. Поэтому инженеры Блумберг добавили сбор этого кеша перед сбором снапшота.

·v8.dev·
Speeding up V8 heap snapshots · V8
How React 18 Improves Application Performance
How React 18 Improves Application Performance
Статья про оптимизации перформанса, которые несет с собой React 18. В статье разбирают проблемы, которые может принести рендер React компонентов, и как их решает React 18 Первая большая фича - это Concurrent Render. React научился разделять рендер на важный и неважный, рендерить в фоне несколько ветвей react-дерева, останавливать рендер (например для обработки пользовательского ввода). React, таким образом, отошел от схемы работы "я рендерю, а весь мир подождет". При рендере "тяжелых" компонентов могло быть заметно проседание перформанса (в статье есть даже интерактивная демка). Теперь же эта проблема решена из коробки. Вторая большая фича - Transitions API. Она позволяет явно описать, какие изменения компонента "неважные", что позволяет React-у оптимизировать рендер Следующая большая фича - серверные компоненты. Про них уже много написано, в целом это позволяет а) не загружать часть кода на клиент б) серверные компоненты тратят ресурсы сервера, а не клиента Далее - Suspense. Хотя Suspense появился еще в 16 реакте, в 18 реакте он стал лучше т.к. нативно-интегрирован с фичами, описанными ранее Также в React появилась фунцкия `cache`, которая позволяет замемоизировать функция в рамках одного рендера. Т.е. если функция, обернутая в `cache`, была вызвана дважды в рамках одного потока рендера, то функция выполнится единожды. Но если функция вызывается в разных рендер-потоках, то будет вызвана несколько раз. Этот же механизм (как я понял) используется в серверных компонентах при загрузке данных через `fetch`. Решение за гранью добра и зла, на мой взгляд.
·vercel.com·
How React 18 Improves Application Performance
In Defence of DOM­Content­Loaded – CSS Wizardry
In Defence of DOM­Content­Loaded – CSS Wizardry
Когда-то событие DOMContentLoaded перестали считать важной метрикой т.к. она чисто техническая и для пользователя она не интересна. Пользователю интересно чтобы контент быстро отрисовался и приложение стало интерактивным. Эта статья рассказывает, что событие DOMContentLoaded все еще может быть очень полезным для анализа производительности сайта. Тот факт, что метрика - техническая, не значит что она не полезна. У нас есть TTFB, которая также техническая метрика, но тем не менее очень ценная для анализа производительности сайта. Автор разбирает, как работает DOMContentLoaded и как это можно использовать. Событие возникает, когда все синхронные и deferred скрипты завершили свое выполнение. При этом в Navigation Timing API у нас есть 2 события - `domContentLoadedEventStart`, аналогичное DOMContentLoaded и `domContentLoadedEventEnd` - время когда обработчик `DOMContentLoaded` завершил работу. Также мы знаем, что есть событие `domInteractive`, которое, по сути, возникает когда браузер закончил чтение HTML страницы. Из этих данных мы можем выяснить следующие цифры: * `domContentLoadedEventStart - domInteractive` - время, необходимое на загрузку и исполнение deferred скриптов * `domContentLoadedEventEnd - domContentLoadedEventStart` - время необходимое на запуск обработчие DOMContentLoad Т.к. `DOMContentLoaded` предшествует последующим событиям (запуск приложения, отрисовка), то очень важно следить за этой и связанными метриками и оптимизировать их. Например, уменьшить размер HTML, количество блокирующих JS-файлов, инлайн маленьких JS-файлов (чтобы убрать пенальти на время сетевого запроса). Любое улучшение метрики улучшает все последующие метрики (практически все), поэтому имеет смысл уделять внимание `DOMContentLoaded`, хотя многие современные гайды по производительности не уделяют внимания этому событию
·csswizardry.com·
In Defence of DOM­Content­Loaded – CSS Wizardry
What is a CDN? An Unbiased Guide to Content Delivery Networks
What is a CDN? An Unbiased Guide to Content Delivery Networks
Хорошая статья, объясняющая, что такое CDN и зачем он нужен. CDN - это сеть глобально-распределенных серверов, которые хостят ваш контент и быстро отдают его посетителям. Зачем это нужно. Представьте, что у вас есть производство и 1 большой склад для всей продукции, которую покупают по всему миру. Когда клиент из другой части света покупает что-то, ему приходится очень долго ждать. Поэтому крупные компании имеют локальные склады. Примерно тоже самое верно и для интернет-трафика. Для того, чтобы пользователь из Индии смог посмотреть сайт из Новосибирска, интернет трафик пройдет большой путь. Этот путь можно сократить, если уметь раздавать тот же самый трафик ближе к пользователю. Этим как раз и занимается CDN. CDN решает проблему быстрой доставки контента до пользователя, что дает следующие преимущества: 1. Нагрузка распределяется по серверам, вместо загрузки 1го сервера 2. CDN умеют оптимизировать трафик (корректно кешировать и сжимать его) 3. CDN умеет бороться с DDoS При внедрении CDN следует следить за следующими аспектами: - Ресурсы должны грузиться. Бывает, что при неверной конфигурации CDN часть ресурсов не может загрузиться - Метрики производительности (TTFB, LCP) должны улучшиться - Не должно быть ошибок в консоли (CORS, неверные заголовки)
·calibreapp.com·
What is a CDN? An Unbiased Guide to Content Delivery Networks
300ms Faster: Reducing Wikipedia's Total Blocking Time
300ms Faster: Reducing Wikipedia's Total Blocking Time
Статья про то, как команда wikipedia обнаружила, что их метрика Total Blocking Time на некоторых страницах достигает 600мс, что выше рекомендованной на 400мс и как они это исправляли. Что такое Total Blocking Time. Google Chrome любые таски, которые длятся дольше 50мс, помечает как long task. Сумма превышения тасками черты long task между первой отрисовкой и тем, как страница стала интерактивной (между FCP и TTI) и является Total Blocking Time. Например, страница отрисовалась, затем JS код выполняет 3 таски - 80мс, 30мс и 100мс. И затем страница становится интерактивной. В этом случае TBT будет = (80-50) + (100-50) = 80мс - столько времени таски превышали лимит в 50мс. Для некоторых страниц википедии TBT составлял 600мс. Просто безумно большое число. Эта проблема была решена в 2 этапа. 1. Было найдено, что 456мс забирает функция `_enable`. Wikipedia использует jquery, и эта функция находила все ссылки и накидывала им кастомный обработчик. На некоторых страницах находятся тысячи ссылок, что делает запуск этой функции долгой задачей. Хуже всего, что этот код не необходим, т.к. буквально за ним идет подписка на `hashchange`, который делает ровно то же самое, что и навешиваемый обработчик. Поэтому этот код был беспощадно удален, что дало улучшение в 200мс. 2. Дальше при исследовании было найдено, что функция `initMediaViewer` занимает 114мс. Там примерно та же проблема, что и в пункте 1. А конкретно - навешивается куча обработчиков слишком рано. Вместо этого используется возможность делегерирования событий. Обработчик накидывается только на контейнер с картинками. Обработчик по `event.target` определяет, какую конкретно картинку необходимо подгрузить В целом wikipedia избавились от 300мс в метрике TBT. Это все еще много (больше рекомендаций), но тут важно, что причина катастрофически низкой производительности как правило в том, что никто просто ей не занимается. Оптимизации в статье самые базовые и легко могли быть сделаны раньше.
·nray.dev·
300ms Faster: Reducing Wikipedia's Total Blocking Time
State of Node.js Performance 2023
State of Node.js Performance 2023
Анализ производительности разных версий nodejs (16, 18 и кое-где 20). Анализ проводился в трех категориях: - Скорость работы внутрянки node.js - Запуск опен-сорс бенчмарка для node.js - Скорость работы сервера (без логики) В целом результаты следующие: - Node.js стал быстрее в большинстве кейсов. Где-то на чуть-чуть (5-10%), а где то на 400-500% (тесты на скорость доступа к переменной) - Кое где Node.js стал медленнее. Например, чтение файла с utf-8 кодировкой намного медленнее чем с ascii кодировкой. Например, заметен регресс производительности в 18 ноде в работе с потоками и буферами. В 20 ноде частично пофиксено Замеры производительности всегда синтетические. Даже если все графики показывают большое увеличение производительности, на реальных приложениях это скажется не так сильно (не стоит ожидать ускорения в 50-100%). Скорее стоит ожидать, что сама платформа становится все более производительной. Теперь при обнаружении проблем производительности намного меньше шанс, что проблема внутри node.js, а не в вашем коде Рекомендую к прочтению тем, кто пишет на node.js. Возможно статья даст вам идеи, как ускорить ваше приложение.
·blog.rafaelgss.dev·
State of Node.js Performance 2023
Introducing Deopt Explorer - TypeScript
Introducing Deopt Explorer - TypeScript
Статья от команды Typescript, как они исправляли код, который деоптимизировался в движке v8 (это который в nodejs и хроме). Если коротко, то движок категоризирует объекты в 3 категории: моноформные (всегда стабильная форма), полиморфные (несколько форм, например могут отличаться пара полей) и мегаморфные (структура объекта очень разнообразна). В зависимости от категории движок может применять различные оптимизации. Для мегаморфных структур оптимизаций нет - они медленные. Когда движок находит, что структура объекта на самом деле имеет большую вариацию и переводит объект в следующую категорию, это называется деоптимизация. Команда TS решила исправлять мегаморфные структуры т.к. они значительно могут влиять на перформанс TS компилятора. Для поиска подобных проблем, оказывается, есть инструменты и расширения для vscode - достаточно запустить скрипт и можно посмотреть прямо в vscode, какие переменные являются мегаморфными по структуре. Команда TS обнаружила, например, что Symbol является мегаморфным, хотя, по идее, так быть не должно. Благодаря инструментарию они нашли, что в Symbol есть необязательные поля, которые добавляются уже инстансированным объектам и это, как минимум, выводит их из разряда мономорфных (раньше была одна структура, затем её расширили). В конструктор Symbol были добавлены новые поля со значением undefined. Хотя, с точки зрения клиентского кода, разницы нет - разница есть для движка. Это оптимизация уменьшает вариативность Symbol, но повышает потребление памяти. В статье очень подробно описано как запускать такой профайлинг, как с ним работать и искать проблемы. Команда TS смогла ускорить компиляцию на 8-10% благодаря исправлению деоптимизаций движка. Рекомендую к прочтению и сохранению в закладки.
·devblogs.microsoft.com·
Introducing Deopt Explorer - TypeScript
Practical Guide to not Block the Event Loop
Practical Guide to not Block the Event Loop
Гайд по неблокирующей работе с event loop. Event Loop можно нечаянно заблокировать каким-нибудь сложным вычислением, что приведет к деградации работы сервиса. Чтобы этого избежать можно использовать 2 решения. Первое решение: сделать ресурсоёмкий код асинхронным, т.е. чтобы он выполнялся не за один "тик" цикла событий, а сделать так чтобы выполнялся итерационно с постановкой задачи в цикл событий. Тогда ресурсоёмий код не будет блокировать цикл событий и движок сможет разобрать дргуие события Второе решение: вынести ресурсоёмкий код в worker. У worker'ов есть свои ограничения (недавно в канале была хорошая ссылка на этот счет), но зато есть один неоспоримый плюс - worker не блокирует цикл событий.
·bbss.dev·
Practical Guide to not Block the Event Loop
Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%
Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%
Amazon Prime Video мигрировали с лямбда-функций (возможно не совсем корректный термин) на монолит и уменьшили требования к ресурсам инфраструктуры на 90% В чем проблема: у Amazon Prime Video есть система мониторинга проблем на стримах. Она состоит из 3х частей: первая часть следит за стримами и отсылает кадры и аудио в S3 хранилищ, вторая часть (детектор) ищет аномалии в этих данных (фризы, потери данных и тд), а третяя часть оркестрирует все это. Эта архитектура была выбрана, потому что такое решение можно сделать быстро и оно легко масштабируется. Однако, такое решение слишком дорого обходится, поэтому команда приняла решение совместить код который извлекает данные из стрима и находит аномалии в рамках одного процесса - теперь данные передаются быстро (без заливки на S3) и стоимость инфраструктуры снизилась на 90% Еще 1 кейс в копилку кейсов "монолит проще чем распределенные системы". Хотя, на мой взгляд (я не архитектор и книжек с кабаном не читал) решение использовать S3, вместо того чтобы поставить близкую функциональность близко друг к другу, выглядит странно
·primevideotech.com·
Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%
Making Tanstack Table 1000x faster with a 1 line change
Making Tanstack Table 1000x faster with a 1 line change
Ускорение Tanstack Table в 1000 раз одной строчкой кода! Автор использует Tanstack Table в своем приложении с большими таблицами и заметил, что приложение подвисает на 30-40 секунд, если включить группировку колонок в таблице. Он пошел разбираться, используя старый дедовский метод выводя тайминг в console, почему так лагает. В процессе поиска он постоянно натыкался на места, которые казались ему медленными. "Ага, тут рекурсивный цикл, это точно он!" (нет). "Ага, это reduce вместо обычного цикла!" (нет). В итоге выяснил, что для трех сгруппированных колонок вызывается метод `groupBy`, каждый из которых отрабатывает по 10 секунд. Оказалось, что группировка лагала из-за использования оператора spread `map.set(resKey, [...previous, row])`. Этот оператор используется в reduce для добавления элемента к массиву в одном из значений `Map`. Оператор spread создает новый массив. Поэтому, например, если в результирующем массив будет 1000 элементов, будет создано 1000 массивов. В данном коде не нужна была иммутабельность, поэтому 1 строчное изменение spread оператора на push (`previous.push(row)`) ускорило код в 1000 раз (10 секунд = 10 ms).
·jpcamara.com·
Making Tanstack Table 1000x faster with a 1 line change
Speeding up the JavaScript ecosystem - npm scripts
Speeding up the JavaScript ecosystem - npm scripts
Продолжение серии статей про ускорение экосистемы JS. На этот раз выбор автора пал на то, как npm запускает скрипты (например через `npm run`). Оказывается, для того чтобы запустить скрипт npm тратит около 400мс. С одной стороны, не так уж много, с другой стороны, что там делать 400мс? Автор проводит несколько оптимизаций, которые уменьшают время запуска до 137мс. Но возможности оптимизаций ограничены временем на борьбу с архитектурой пакета npm. Если писать подобное решение с нуля, удалив все "лишнее", то можно уместиться в 22 мс. Что конкретно сделал автор. 1. Классическая проблема JS экосистемы - все `require` и `import` описываются в начале файла, что заставляет движок сразу подгружать код, хотя он может быть еще не нужен. В npm эта проблема так же повторяется - мы просто хотим запустить скрипт, но npm делает require разных утилок, которые, например, нужны для обработки ошибок. Простой перенос require в то место, где код действительно понадобится, позволяет ускорить работу основного скрипта. 2. Класс, который обрабатывает команды npm, является супер-классом и тянет в себя много функционала. Вы хотите просто запустить скрипт? Держите audit, dedupe, workspaces и другие крутые функции, которые вам сейчас не нужны. Вынос этого функционала дал 20мс 3. Еще 1 вещь, которая замедляет npm - это сортировка строк. Опять же, хороший вопрос, зачем нам там что-то сортировать, когда мы вызываем конкретный скрипт из package.json. Для сортировки используется `Intl. Collator`, это позволяет сортировать строки с учетом локали пользователя. Но там сортируется массив названий команд и с этим хорошо справляется обычный `.sort`. Таким образом выиграли еще 10 мс. К этому моменту время запуска сократилось до 200мс 4. Следующий найденный, но не исправленный момент - установка названия процесса. Чтобы процесс не назывался `npm` или `node`, npm ставит процессу корректное название, позволяющее легче с ним работать, например, при мониторинге работы процессов. Это очень полезная фича, но она занимает 10мс 5. Дальше было найдено, что функция glob также тратит много времени работы. npm управляет логами запуска скриптов и это очень полезная фича. Иронично, что она тратит много времени на сортировку массива из десятка элементов через `Inlt.Collator`. Замена на обычный `.sort()` сохранила еще 10 мс. К этому момент время запуска сократилось до 137мс (не спрашивайте "как?", я тоже не понял, но вот между 3 и 5 шагом есть всего 1 оптимизация на 10мс, но разница - 63мс. Возможно там 6 раз делалась сортировка, возможно автор делал что-то еще, что решил не отображать в статье) Дальше автор приходит к выводу, что для дальнейшего ускорения нужно заняться очень агрессивным удалением или комментированием кода. Поэтому он пишет с нуля свою минимальную имплементацию и получает 22мс.
·marvinh.dev·
Speeding up the JavaScript ecosystem - npm scripts
Почему Banditypes — самая маленькая TS-библиотека для валидации схем
Почему Banditypes — самая маленькая TS-библиотека для валидации схем
Статья про создание самой маленькой либы для валидации схем. В целом это статья не про валидацию схем, а про проектирование библиотеки с минимальным размером. Конкретно здесь автор влез в 400 байт.
·habr.com·
Почему Banditypes — самая маленькая TS-библиотека для валидации схем
Stop Obsessing Over Development Velocity, Focus on This Instead - Itamar Gilad
Stop Obsessing Over Development Velocity, Focus on This Instead - Itamar Gilad
Часто менеджмент ставит цель "ускорить разработку". Ну т.е. ускорить либо velocity команды, либо скорость запуска фич. В данной статье рассматривается, почему это желание (ускорить разработку) - это вредно. Статья написана немного в стилей agile коучей (собственно автор и есть коуч) и поэтому в статье присутствуют манипулятивные приёмчики (в основном связанные со значениями цифр). Но в чем суть и что предлагает автор. Давайте представим, что у нас есть 2 команды: А и Б. В команду А мы нанимаем лучших аджайл коучей с рынка и начинаем коучить их, чтобы они учились делать как можно больше фич за то же время. Команду Б мы коучим, чтобы они проводили более качественное исследование того, что надо сделать. Теперь, нам нужно принять правду об IT-разработке: из всех фич, что мы делаем, большинство вообще не имеют влияния на бизнес, а меньшая часть делится на те, которые негативно влияют и те, которые позитивно влияют. В здоровом бизнесе фич с позитивным влиянием должно быть больше. Команда А действительно начнет делать больше фич, за тот же период времени и количество "хороших" фич в абсолютном значении будет больше. Условно, раньше за год делали 6 фич, которые приносили деньги, теперь 6 фич. Команда Б же будет тратить больше времени на исследования (а то ли мы делаем? а что хочет пользователь? и тд и тп), но зато теперь эта команда будет делать больше (пропорционально) полезных фичей. В итоге, хотя команда Б в целом делает фичей меньше, её влияния на бизнес будет более ценным. Автор подчеркивает 2 важных мысли: - "do the right things" - "our job is to minimize output, and maximize outcome and impact" В принципе, если вам надо убедить коллег, что надо не делать больше, а делать нужное, то статья очень хорошо показывает, как объяснять это графиками и цифрами обычным людям. Можно просто брать и повторять.
·itamargilad.com·
Stop Obsessing Over Development Velocity, Focus on This Instead - Itamar Gilad
Speeding up the JavaScript ecosystem - eslint
Speeding up the JavaScript ecosystem - eslint
3 пост в серии "ускорение JS экосистемы". Напомню, что автор доказывает, что не надо переписывать JS экосистему на go, rust, etc, вместо этого следует заняться оптимизацией текущей экосистемы на JS. В этот раз автор ускоряет eslint - инструмент, который есть, наверное, в каждом крупном проекте. Было найдено несколько проблем. 1. Поиск нужного токена комментария в правиле JSDoc Почему-то при парсинге комментариев для правила проверки JSDoc класс `BackwardTokenCommentCursor` аллоцируется более 20 миллионов раз. Переделка работы поиска нужного токена вынесена из текущего поста, но в целом, главная проблема - это лишние вызовы класса. Т.к. аллоцируется так много элементов то поиск по ним идет долго. В коде eslint используется метод `findIndex`, который линейно перебирает токены. Автор переписал поиск на бинарный поиск и ускорил работу этого участка кода в 2 раза. 2. Движок селекторов В eslint есть поддержка синтаксиса для матчинга AST-нод аля "css-селекторы". Это достаточно удобная фича, но движок селекторов также требует оптимизации. Медленный участок кода в селекторе - функция `getPath`. Это функция, которая достает свойство объекта по его строковому пути. Например `getPath({a: {b: true}}, 'a.b') === true`. В этой функции было 2 проблемы, которые влияли на перформанс: 1. Старый транспайленный код. Проект, видимо, давно не публиковался с новыми транспайлерами с таргетом под современные рантаймы, а цикл `for-of` при транспиляции заменяется на генераторы, что сильно медленнее нативного `for-of`. Это частая проблема в экосистеме. Замена на for-of значительно ускорила код. 2. `for-of` делается по `path.split('.')`, что вызывает алокации памяти. Вместо этого автор переписал код на ручной проход по строке с поиском точек и выделением части пути. Итог этой оптимизации: 2.7с = 486мс Также был сделан ранний выход из движка. Движок селекторов очень мощный, но часто при написании eslint правил разработчикам достаточно матчить только тип AST-ноды, а это можно делать вообще без запуска движка. Далее автор приходит к логичной мысли, что eslint когда-то решил что удобно писать точечные селекторы с css-семантикой. В целом это удобно, но eslint также мог бы дать простое API для JS-селекторов. Ну т.е. зачем нам придумывать какой-то движок, повторяющий семантику css селекторов, если можно просто писать нативный JS код, проверяющий что нам нужно. Автор проверил, будет ли это работать быстрее - и это действительно так. Простая JS функция в 30 раз быстрее, чем движок селекторов. 3. Конвертация AST в AST Eslint использует свой формат AST, в который другие парсеры вынуждены переводить свои AST. И если babel-parser имеет почти такой же AST, то вот typescript-parser имеет свой AST и при парсинге файлов переводит свой TS-AST в расширенный AST для eslint. Это влияет на перформанс дважды: нужно хранить 2 дерева AST и нужно делать дополнительную работу по переводу AST в AST. Автор сравнил скорость работы typescript eslint parser и babel eslint-parser и выяснил, что последний работает в три раза быстрее. Так что, если вам не нужны правила, которые имеют информацию о типах (киллер фича typescript parser, он, например, позволяет проверять, что никто не проходит for in по массиву), то можете заменить парсер на babel и ускорить свой линтинг. Рекомендую к прочтению в оригинале. Хорошая статья и хорошие примеры оптимизации перформанса.
·marvinh.dev·
Speeding up the JavaScript ecosystem - eslint
Как на практике работать над перфомансом веб-приложения: опыт Авто.ру
Как на практике работать над перфомансом веб-приложения: опыт Авто.ру
Текстовая версия доклада от Авто.ру про реальный кейс улучшения перформанса веб-приложения. Автор последовательно показывает методику ускорения Авто.ру. Сначала флоу приложения декомпозировали на этапы (серверный код, рендер, css, js) и в каждом отдельном этапе уже искали возможно для улучшения перформанса. Кроме приёмов для ускорения, также показаны и рассказаны инструменты, с помощью которых можно профилировать перформанс приложения. Рекомендую к прочтению т.к. это реальный кейс улучшения перформанса реального сайта, а не теоретические выкладки.
·habr.com·
Как на практике работать над перфомансом веб-приложения: опыт Авто.ру
The truth about CSS selector performance
The truth about CSS selector performance
Статья от Microsoft про влияние css-селекторов на производительность страницы и про оптимизацию перформанса стилей. Статья объясняет: - Как браузер обрабатывает селекторы и как они влияют на производительность - Какие инструменты есть в браузере для профилирования проблем с производительностью - На специальной демке наглядно, с цифрами, показывается, как "медленные" css-стили могут повлиять на перформанс и как это исправить Рекомендую к прочтению - очень хороший гайд по достаточно редкой теме.
·blogs.windows.com·
The truth about CSS selector performance
A beginner’s guide to Chrome tracing
A beginner’s guide to Chrome tracing
Короткая заметка про встроенную в chromium инструмент для трейсинга загрузки страниц. Это достаточно низкоуровневый инструмент (навигация через wasd и использование внутренних терминов chromium), который может пригодится. когда вам не достаточно мощности стандартных дев-тулзов хромиума. Например, tracing позволяет найти медленные css-селекторы и по профилировать влияние работы внутренних подсистем (например, проверка орфографии). Вряд ли много кому придется это использовать, но знать о такой возможности - полезно
·nolanlawson.com·
A beginner’s guide to Chrome tracing
Speeding up the JavaScript ecosystem - one library at a time
Speeding up the JavaScript ecosystem - one library at a time
Сейчасть есть модный тренд на переписывание инструментов для сборки на Rust или Go. Типа если мы перепишем инструмент на язык, который ближе к железу, то он будет работать быстрее. Однако JIT в текущих JS движках тоже достаточно быстрый, и возможно проблема не в языке, на котором написан инструмент, но в самом коде. В статье автор разбирает, как он с помощью профайлера находил проблемы с перформансом в текущих библиотеках и как исправлял код в них, чтобы они работали быстрее. В статье достаточно подробное описание, что за проблема, почему это проблема и как её решать. Также есть ссылки на PR-ы в инструменты Честь и хвала таким героям, которые оптимизируют инструменты, которые афектят на все сообщество. Основной посыл статьи - вместо того, чтобы переписывать инструменты на другие языки, следует все таки посмотреть, что можно оптимизировать в текущем стеке. Рекомендую к прочтению
·marvinh.dev·
Speeding up the JavaScript ecosystem - one library at a time