Хотел сегодня написать про проблемы разгона спред оператора, но история эта довольно большая и затрагивает еще несколько оптимизаций. Сегодня дам поверхностную инфу, а на следующей неделе будем разбирать все детально.
Скрины перф тестов перед вами. Первый скрин “до” - сильно оптимизированная версия нового реатома (см `reatom3`) с точки зрения архитектуры и используемых структур данных. Меня не покидало ощущение что результаты должны быть лучше и после долгих копаний и перф дебага нашел две проблемы:
- копирование объектов лучше делать через ручное перечисление всех свойств
- нативный forof быстрее транспилированной версии и forEach вместе взятых.
Результат - либа стала тупо в два раза быстрее, см. абсолютные значения (`med` справа) на втором скрине.
Скрины перф тестов перед вами. Первый скрин “до” - сильно оптимизированная версия нового реатома (см `reatom3`) с точки зрения архитектуры и используемых структур данных. Меня не покидало ощущение что результаты должны быть лучше и после долгих копаний и перф дебага нашел две проблемы:
- копирование объектов лучше делать через ручное перечисление всех свойств
- нативный forof быстрее транспилированной версии и forEach вместе взятых.
Результат - либа стала тупо в два раза быстрее, см. абсолютные значения (`med` справа) на втором скрине.
👍12🔥2
Профилирование производительности
Хотел бы я рассказать о том как просто, быстро и весело это делать и что результат оправдывает все ожидания, но история будет только про хороший результат, а вот что бы его достичь придется покрутиться.
У меня есть бенчмарки для реатома и их результаты не всегда меня удовлетворяют. В очередном заходе на оптимизацию библиотеки я решил попрофилировать перф через нормальные инструменты, а не просто случайными переписками кода.
План был такой: посмотреть в профайлер от квоки, увидеть прожорливое место в коде и исправить ошибку, которую я, видимо, не заметил раньше.
По факту все было дольше и вот почему:
1) Из стандартного отсортированного списка вызовов функций я ничего понять не смог. Пошел изучал флеймграф. Там тоже мало что понятно, пока не не схлопнешь одинаковые вызовы вместе (
2) В бенче происходило много лишней работы, кроме самого теста реатома, которая мусорила в результаты. Для удобства чтения я закомментировал не важный для анализа код.
3) Когда я нашел прожорливое место перейти в исходники мне не удалось, видимо сурсмапы не подтягивались. Понимать минифицированный код было очень сложно, а преттиер не хотел его форматировать из-за какой-то ошибки парсинга. К счастью, у меня был установлен Rome и он смог отформатировать код к более понятному состоянию.
4) Я перезапустил профайлер и переоткрыл флеймграф, но ссылка на функцию потребляющую много ресурсов все еще вела на первую строчку, как будто код все еще минифицирован. Помогло только
5) Дальше меня ждал большой сюрприз, функция которая потребляля треть перфа в большом пайплайне операций была очень тривиальной и я все никак не мог понять в чем там проблема. Тк. профилировщик дает ссылку не на конкрутную проблемную операцию, а на содержащую ее функцию, я решил разбить внутрении операции на отдельные функции, а для удобочитаемости минифицированного кода я переписал функцию на методы тестового объекта - минификатор не меняет имена свойств, только переменные.
6) Повторяем п. 4 и вуаля! Проблема найдена. В минифицированной версии спред заменялся на свою реализацию с Object.assign (или полифилом).
Осталось лишь переписать копирование объекта на ручной перебор всех свойств и флеймграф стал более равномерным, ура-ура!)
P.S. Я попробовал запустить бенч с импортом из исходников, а не билда, для нормальной работы сурсмапов, но сами результаты тестов в этом случае едут (терсер и другие минификаторы делают некоторые AOT улучшения кода), да и ссылки все равно были на транспилированную версию без типов, которую делает квока под каптом.
P.P.S. После смены таргета билда на современные браузеры спред не транспилируется и работает быстрее, но версия с ручным перебором свойств все равно заметно быстрее.
Хотел бы я рассказать о том как просто, быстро и весело это делать и что результат оправдывает все ожидания, но история будет только про хороший результат, а вот что бы его достичь придется покрутиться.
У меня есть бенчмарки для реатома и их результаты не всегда меня удовлетворяют. В очередном заходе на оптимизацию библиотеки я решил попрофилировать перф через нормальные инструменты, а не просто случайными переписками кода.
План был такой: посмотреть в профайлер от квоки, увидеть прожорливое место в коде и исправить ошибку, которую я, видимо, не заметил раньше.
По факту все было дольше и вот почему:
1) Из стандартного отсортированного списка вызовов функций я ничего понять не смог. Пошел изучал флеймграф. Там тоже мало что понятно, пока не не схлопнешь одинаковые вызовы вместе (
Toggle left-heavy view).2) В бенче происходило много лишней работы, кроме самого теста реатома, которая мусорила в результаты. Для удобства чтения я закомментировал не важный для анализа код.
3) Когда я нашел прожорливое место перейти в исходники мне не удалось, видимо сурсмапы не подтягивались. Понимать минифицированный код было очень сложно, а преттиер не хотел его форматировать из-за какой-то ошибки парсинга. К счастью, у меня был установлен Rome и он смог отформатировать код к более понятному состоянию.
4) Я перезапустил профайлер и переоткрыл флеймграф, но ссылка на функцию потребляющую много ресурсов все еще вела на первую строчку, как будто код все еще минифицирован. Помогло только
rm /Users/artalar/.quokka/test.cpuprofile5) Дальше меня ждал большой сюрприз, функция которая потребляля треть перфа в большом пайплайне операций была очень тривиальной и я все никак не мог понять в чем там проблема. Тк. профилировщик дает ссылку не на конкрутную проблемную операцию, а на содержащую ее функцию, я решил разбить внутрении операции на отдельные функции, а для удобочитаемости минифицированного кода я переписал функцию на методы тестового объекта - минификатор не меняет имена свойств, только переменные.
6) Повторяем п. 4 и вуаля! Проблема найдена. В минифицированной версии спред заменялся на свою реализацию с Object.assign (или полифилом).
Осталось лишь переписать копирование объекта на ручной перебор всех свойств и флеймграф стал более равномерным, ура-ура!)
P.S. Я попробовал запустить бенч с импортом из исходников, а не билда, для нормальной работы сурсмапов, но сами результаты тестов в этом случае едут (терсер и другие минификаторы делают некоторые AOT улучшения кода), да и ссылки все равно были на транспилированную версию без типов, которую делает квока под каптом.
P.P.S. После смены таргета билда на современные браузеры спред не транспилируется и работает быстрее, но версия с ручным перебором свойств все равно заметно быстрее.
👍4🤔2
Есть такой уже старый и почти заброшенный ponyfoo.com, которым я восторгался в свое время. Там можно найти множество глубоких и понятных статей по теме фронтенда и далеко не все из них уже устарели.
Например, туда писал Benedikt Meurer, один из разработчиков v8.
Или вот еще пара статей:
Polyfills or Ponyfills?
The JavaScript Standard
Например, туда писал Benedikt Meurer, один из разработчиков v8.
Или вот еще пара статей:
Polyfills or Ponyfills?
The JavaScript Standard
👍4
artalog
Хотел сегодня написать про проблемы разгона спред оператора, но история эта довольно большая и затрагивает еще несколько оптимизаций. Сегодня дам поверхностную инфу, а на следующей неделе будем разбирать все детально. Скрины перф тестов перед вами. Первый…
А по поводу проблем с производительностью спред оператора есть такие баги:
https://bugs.chromium.org/p/v8/issues/detail?id=10763
https://bugs.chromium.org/p/chromium/issues/detail?id=1204540
Выдержка от @cevek:
видимо проблема в том что спред создает новый объект не соответветсвующей мапе к оригинальному
The problem is that the CloneObjectIC creates local copies of object literal maps instead of reusing the shared trees from the cache. The following should be true but isn't:
от этого случается мегаморфизм и прощай перформанс
https://bugs.chromium.org/p/v8/issues/detail?id=10763
https://bugs.chromium.org/p/chromium/issues/detail?id=1204540
Выдержка от @cevek:
видимо проблема в том что спред создает новый объект не соответветсвующей мапе к оригинальному
The problem is that the CloneObjectIC creates local copies of object literal maps instead of reusing the shared trees from the cache. The following should be true but isn't:
~/v8$ v8 --allow-natives-syntax --nolazy-feedback-allocation
d8> o = {a:1, b:2}; %HaveSameMap({...o}, {...o})
false
от этого случается мегаморфизм и прощай перформанс
👍6
2022-05-24
artalog
Про легаси компоненты реакта, тестирование компонентов, конкурентные очереди в вебе и закрытые камьюнити
🔥2
deoptigate
Крутой инструмент, который генерит простые репорты о проблемах в JIT оптимизациях запущенного кода.
У меня удалось запустить проект только на 14 ноде, проблем особых не выявилось, но есть несколько мест для лучшей оптимизации.
(запускал на этом файле)
Крутой инструмент, который генерит простые репорты о проблемах в JIT оптимизациях запущенного кода.
У меня удалось запустить проект только на 14 ноде, проблем особых не выявилось, но есть несколько мест для лучшей оптимизации.
(запускал на этом файле)
🔥5
О канале
рекламу не даю
Привет, меня зовут Артём Арутюнян aka @artalar, я разрабатываю крупные ИТ-сервисы больше 10 лет, половину из которых программированием на JS. Выступаю на конференциях. Участвовал во множестве разнообразных проектах в роле системного администратора, девопса, продукта, менеджера, техписа, разработчика и лида. Сейчас работают линейным фронтендером, а в свободное время сфокусирован на разработке менеджера состояния Reatom.
В этом канале я каждый день рассказываю о сложностях и мыслях с которыми сталкиваюсь в повседневной работе, своих петах и комьюнити разработчиков.
Вот самые ценные материалы за все время:
- исправление уязвимости в nanoid
- архитектура и реактивное программирование
- кто такой лид
- простая и эффективная интернационализация
- gitpod
- cостояние на клиенте
- Temporal proposal (статья на английском)
- архитектура веба
- не функциональные требования
Еще интересное:
- headless ui
- доступность как архитектура UI
- иммутабельные и трансисдентные структуры данных
- про effector
- слоты в реакте
- Когда нужен SSR
- Декларативное программирование
- оценка производительности библиотек
- Service Worker для блога
- история микрохакатона
- что такое декларативное программирование
- оптимизации минификатора
- JavaScript empty mark
- простота кода
- синтаксис и семантика в программировании
- сложность API
- IoC & DI
- технологий: Hasura, Rome, SWC, linkedom, Preact signals, nx...
Иногда, случаются стихийные войсы на тему последних постов или просто обсудить чью-то боль. Например, вот детальное описание моего опыта с Hasura или дискуссия с Ильёй Климовым об архитектуре системы пермишенов.
Есть платный чат artalogg с еженедельными стримами для глубокого погружения: https://news.1rj.ru/str/artalog/1750
рекламу не даю
Привет, меня зовут Артём Арутюнян aka @artalar, я разрабатываю крупные ИТ-сервисы больше 10 лет, половину из которых программированием на JS. Выступаю на конференциях. Участвовал во множестве разнообразных проектах в роле системного администратора, девопса, продукта, менеджера, техписа, разработчика и лида. Сейчас работают линейным фронтендером, а в свободное время сфокусирован на разработке менеджера состояния Reatom.
В этом канале я каждый день рассказываю о сложностях и мыслях с которыми сталкиваюсь в повседневной работе, своих петах и комьюнити разработчиков.
Вот самые ценные материалы за все время:
- исправление уязвимости в nanoid
- архитектура и реактивное программирование
- кто такой лид
- простая и эффективная интернационализация
- gitpod
- cостояние на клиенте
- Temporal proposal (статья на английском)
- архитектура веба
- не функциональные требования
Еще интересное:
- headless ui
- доступность как архитектура UI
- иммутабельные и трансисдентные структуры данных
- про effector
- слоты в реакте
- Когда нужен SSR
- Декларативное программирование
- оценка производительности библиотек
- Service Worker для блога
- история микрохакатона
- что такое декларативное программирование
- оптимизации минификатора
- JavaScript empty mark
- простота кода
- синтаксис и семантика в программировании
- сложность API
- IoC & DI
- технологий: Hasura, Rome, SWC, linkedom, Preact signals, nx...
Иногда, случаются стихийные войсы на тему последних постов или просто обсудить чью-то боль. Например, вот детальное описание моего опыта с Hasura или дискуссия с Ильёй Климовым об архитектуре системы пермишенов.
Есть платный чат artalogg с еженедельными стримами для глубокого погружения: https://news.1rj.ru/str/artalog/1750
👍33🔥18❤8🤔3
Смерть от тысячи порезов кеширования
Уже давно в разных чатиках @xbgnx высказывает мысль о том что реакт и все остальные библиотеки для рендеринга медленные не потому что в них не достаточно оптимизаций, а потому что их там слишком много и они в своей сумме только тормозят конечное приложение. "Правильный путь" - ререндерить все на каждый чих и не пытаться по дороге что-то мемоизировать. В заголовке поста есть ссылка на похожие мысли от Доддса.
Недавно у меня состоялся еще один разговор, где мне показали очень быстрый фреймворк для построение интерфейсов на Rust - github.com/emilk/egui. В первом же примере ридми сразу бросается в глаза такой код: age += 1. А как потом библиотека понимает что нужно перерисовать места отображения age? А никак, весь шаблон приложения просто целиком пересчитывается с нуля. И это не тормозит!
Как такое может быть и если это правда, почему такой подход ещё не стандарт, спросите вы?
Любое кеширование стоит ресурсов: помимо занимаемой памяти нужно больше процессорных ресурсов на ее обслуживание (GC) и инвалидацию кеша. В продвинутых реактивных системах под капотом используются графы по которым нужно бегать, иногда, по несколько раз.
А зачем нужно кеширование? Для предотвращения избыточных вычислений (если они идемпотентны). И вот в чем хитрость. Алгоритмы обслуживания кешей или реактивных графах выполняются достаточно быстро, кешировать их в вакууме незачем. Те кто познали этот дзен уже достаточно опытные и хорошо понимают что такое сложность алгоритма и как писать оптимальный код. Когда такие люди пишут примеры кода фичи / приложения без кеширования, что бы сравнить перф, их наивная реализация уже является оптимизированной версией, по сравнению с кодом рядового разработчика из массы.
Те если кто-то пишет быстрый код без кеширования, скорее всего он заранее, специально или по привычке, продумал оптимальные структуры данных и алгоритмы их обхода и получившийся результат действительно может быть быстрее того что пишет средний разработчик на оптимальном фреймворке, Но это не объективное сравнение, тк условия разные - разные уровни разработчиков.
По моему опыту, большинство разработчиков пишут не оптимальный код и о его производительности просто не могут беспокоится в должной мере: что-то не знают, на что-то сейчас нет времени. Не важно на каком фреймворке / библиотеке - перегоняться из фичи в фичу или от слоя к слою данные будут с квадратичной сложностью.
Например. У вас есть приложение на редаксе и какой-то маппинг данных не замемоизирован или мемоизация сломана (частый кейс). Ну список чего-то там. При изменени любых других данных: инпута, нотификация пришла, лайк поставили - этот маппинг будет пересчитываться. У среднестатистического разработчинга в таком мапинге будет лежать линейная или квадратичная сложность. Опытный разработчик будет использовать константные мапы или ленивые итераторы и не будет понимать зачем ему все это кеширование.
И тут нужно понять, не получится множество разработчиков научить писать всегда быстрый код, хотя бы потому что мерить и контролировать в автоматическом режиме это очень сложно. Но можно дать им библиотеки, которые сами будут пытаться делать какие-то оптимизации - снимать ответственность и позволять плохокодить.
Это не эффективный подход, но это продуктовый подход и в большенстве своем он работает. Если вы дадите толпам джунов / мидлов писать код без его принудительного кеширования оно очень быстро перестанет хоть как-то ворочиться, я это видел.
Уже давно в разных чатиках @xbgnx высказывает мысль о том что реакт и все остальные библиотеки для рендеринга медленные не потому что в них не достаточно оптимизаций, а потому что их там слишком много и они в своей сумме только тормозят конечное приложение. "Правильный путь" - ререндерить все на каждый чих и не пытаться по дороге что-то мемоизировать. В заголовке поста есть ссылка на похожие мысли от Доддса.
Недавно у меня состоялся еще один разговор, где мне показали очень быстрый фреймворк для построение интерфейсов на Rust - github.com/emilk/egui. В первом же примере ридми сразу бросается в глаза такой код: age += 1. А как потом библиотека понимает что нужно перерисовать места отображения age? А никак, весь шаблон приложения просто целиком пересчитывается с нуля. И это не тормозит!
Как такое может быть и если это правда, почему такой подход ещё не стандарт, спросите вы?
Любое кеширование стоит ресурсов: помимо занимаемой памяти нужно больше процессорных ресурсов на ее обслуживание (GC) и инвалидацию кеша. В продвинутых реактивных системах под капотом используются графы по которым нужно бегать, иногда, по несколько раз.
А зачем нужно кеширование? Для предотвращения избыточных вычислений (если они идемпотентны). И вот в чем хитрость. Алгоритмы обслуживания кешей или реактивных графах выполняются достаточно быстро, кешировать их в вакууме незачем. Те кто познали этот дзен уже достаточно опытные и хорошо понимают что такое сложность алгоритма и как писать оптимальный код. Когда такие люди пишут примеры кода фичи / приложения без кеширования, что бы сравнить перф, их наивная реализация уже является оптимизированной версией, по сравнению с кодом рядового разработчика из массы.
Те если кто-то пишет быстрый код без кеширования, скорее всего он заранее, специально или по привычке, продумал оптимальные структуры данных и алгоритмы их обхода и получившийся результат действительно может быть быстрее того что пишет средний разработчик на оптимальном фреймворке, Но это не объективное сравнение, тк условия разные - разные уровни разработчиков.
По моему опыту, большинство разработчиков пишут не оптимальный код и о его производительности просто не могут беспокоится в должной мере: что-то не знают, на что-то сейчас нет времени. Не важно на каком фреймворке / библиотеке - перегоняться из фичи в фичу или от слоя к слою данные будут с квадратичной сложностью.
Например. У вас есть приложение на редаксе и какой-то маппинг данных не замемоизирован или мемоизация сломана (частый кейс). Ну список чего-то там. При изменени любых других данных: инпута, нотификация пришла, лайк поставили - этот маппинг будет пересчитываться. У среднестатистического разработчинга в таком мапинге будет лежать линейная или квадратичная сложность. Опытный разработчик будет использовать константные мапы или ленивые итераторы и не будет понимать зачем ему все это кеширование.
someList.map(({id}) => anotherList.find(el => el.id === id))
VS
someList.map(el => anotherMap[el.id])
И тут нужно понять, не получится множество разработчиков научить писать всегда быстрый код, хотя бы потому что мерить и контролировать в автоматическом режиме это очень сложно. Но можно дать им библиотеки, которые сами будут пытаться делать какие-то оптимизации - снимать ответственность и позволять плохокодить.
Это не эффективный подход, но это продуктовый подход и в большенстве своем он работает. Если вы дадите толпам джунов / мидлов писать код без его принудительного кеширования оно очень быстро перестанет хоть как-то ворочиться, я это видел.
👍18🤔16🔥1
Буду по пятницам рекомендовать какие-то доклады или подкасты. Этот я ещё не слышал и тема может показаться скучной, но гость (Николай Рыжиков) очень интересный, рекомендую поискать его доклады в ютубе.
👍4
Forwarded from запуск завтра
Помните медицинские карты в больницах? Картонные книжечки, в которые вклеены десятки листочков с анализами и тем самым докторским почерком. Сегодня клиники по всему миру переходят на электронные медицинские карты.
Первый протокол передачи медицинских данных в электронном виде появился в конце 1970-х — на 10 лет раньше веб-протокола HTTP. При этом комплексно задача хранения и передачи медицинских данных не решена до сих пор. Почему?
Разбираемся в новом эпизоде подкаста вместе с российским апологетом самого современного протокола передачи медицинских данных FHIR Николаем Рыжиковым.
Слушайте и подписывайтесь на всех платформах: Apple, Google, Яндекс, Spotify, Castbox, Overcast, веб-версия.
Первый протокол передачи медицинских данных в электронном виде появился в конце 1970-х — на 10 лет раньше веб-протокола HTTP. При этом комплексно задача хранения и передачи медицинских данных не решена до сих пор. Почему?
Разбираемся в новом эпизоде подкаста вместе с российским апологетом самого современного протокола передачи медицинских данных FHIR Николаем Рыжиковым.
Слушайте и подписывайтесь на всех платформах: Apple, Google, Яндекс, Spotify, Castbox, Overcast, веб-версия.
🔥5👍2💩1