Forwarded from Сергей Озеранский
🧠 Как 3 вечера анализа и оптимизаций дали минус 1 CPU и +40% к скорости ответов API
👀 Всё началось с того, что я случайно (ну как случайно, проблема была всегда, просто я первый задался вопросом - "почему?") заметил, что один Python-сервис на staging потребляет до 2.5 CPU.
Для сравнения — весь namespace потребляет около 6 CPU. То есть один сервис ест почти половину ресурсов. И это при том, что это не какой-то нагруженные сервис, это синхронный API-сервис, да еще и без нагрузки, это же staging.
Стало интересно — а что там внутри вообще так жрёт?
📦 Запустил профайлинг CPU через Pyroscope и… понеслось.
UI у Pyroscope меня не устроил — флеймграфы красивые, но неудобные для глубокого анализа.
📥 Поэтому я выгрузил дамп как
Так мне удобнее, быстрее и информативнее.
📊 Профилирование показало несколько важных узких мест:
🧱 Проблема №1: конвертация данных из MySQL
Самое "жирное" место —
Это код, который конвертирует строки из БД в Python-объекты на каждый запрос.
Наиболее затратные конвертации:
-
-
-
-
-
✅ Решение:
📌 Использовал C extension у mysql-connector-python
Официальная документация: https://dev.mysql.com/doc/connector-python/en/connector-python-cext.html
📉 Результат:
- Минус 1 CPU
- До +40% ускорение некоторых endpoint’ов
🧠 Проблема №2: неэффективный Python-код
Пример: функция
- делает кучу проверок в логике
- использует неэффективные структуры данных, например, list для поиска вместо
- обрабатывает сразу множество возможных вариантов логики, в зависимости от входных данных
✅ Решение:
📌 Переписал участок без изменения бизнес-логики:
- заменил структуры данных
- добавил ранние выходы
- убрал дублирующиеся проверки
📌 Cognitive complexity снизилась, временная сложность — тоже. Производительность выросла.
⚠️ Проблема №3: код, который не нужен, но работает
Профайлинг показал, что куча ресурсов уходит на код, который вообще не должен уже как год использоваться. Но код активно выполняется. WTF!?
🤷♂️ Функции вызываются, результат — пустой, но код исполняется. Причём часто и тяжело.
✅ Решение:
📌 Удалил мёртвый код, обновил импорты, подчистил зависимости.
📌 Поднял вопрос о полной деактивации этого кода и мы таки это сделали — ресурсы можно использовать лучше.
🐘 Проблема №4: Импорты. Много. Дорого.
Во время анализа я наткнулся на ещё одну тихую, но дорогую проблему — огромные ресурсы уходят на фазу импорта модулей в Python.
Конкретно — на
📌 Почему это важно?
- Импорты выполняются на каждый старт сервиса.
- Чем жирнее и зависимее ваши модули, тем дольше и тяжелее проходит импорт.
- Это не всегда очевидно, но можно видеть в профайлинге:
📊 У нас в сервисе модулей реально много.
Многое из них – просто "свалены в кучу", где-то грузятся тяжёлые зависимости. Да и сложная структура проекта вынуждает иметь большое количество импортов.
И, как итог, CPU тратится на то, что можно было бы стремиться избежать.
✅ Что с этим делать:
- Разделять модули по функциональности.
- Отложенные импорты (lazy import) – практика может применяться (но есть нюансы), если модуль нужен только в конкретной функции.
- Минимизировать зависимости и импорт только того, что действительно нужно.
- Следить за импортами в
Следующий шаг: memory профайлинг. Pyroscope в нашей инфрастуктуре такое не умеет и нужно приседать, но надеюсь дойдут руки и до RAM.
👀 Всё началось с того, что я случайно (ну как случайно, проблема была всегда, просто я первый задался вопросом - "почему?") заметил, что один Python-сервис на staging потребляет до 2.5 CPU.
Для сравнения — весь namespace потребляет около 6 CPU. То есть один сервис ест почти половину ресурсов. И это при том, что это не какой-то нагруженные сервис, это синхронный API-сервис, да еще и без нагрузки, это же staging.
Стало интересно — а что там внутри вообще так жрёт?
📦 Запустил профайлинг CPU через Pyroscope и… понеслось.
UI у Pyroscope меня не устроил — флеймграфы красивые, но неудобные для глубокого анализа.
📥 Поэтому я выгрузил дамп как
pprof
файл и открыл его через go tool pprof
.Так мне удобнее, быстрее и информативнее.
📊 Профилирование показало несколько важных узких мест:
🧱 Проблема №1: конвертация данных из MySQL
Самое "жирное" место —
conversion.py
→ MySQLConverter.row_to_python
.Это код, который конвертирует строки из БД в Python-объекты на каждый запрос.
Наиболее затратные конвертации:
-
_DECIMAL_to_python
-
_INT_to_python
-
_JSON_to_python
-
_DATETIME_to_python
-
_STRING_to_python
✅ Решение:
📌 Использовал C extension у mysql-connector-python
Официальная документация: https://dev.mysql.com/doc/connector-python/en/connector-python-cext.html
📉 Результат:
- Минус 1 CPU
- До +40% ускорение некоторых endpoint’ов
🧠 Проблема №2: неэффективный Python-код
Пример: функция
change_type
, которая:- делает кучу проверок в логике
- использует неэффективные структуры данных, например, list для поиска вместо
set
/ dict
- обрабатывает сразу множество возможных вариантов логики, в зависимости от входных данных
✅ Решение:
📌 Переписал участок без изменения бизнес-логики:
- заменил структуры данных
- добавил ранние выходы
- убрал дублирующиеся проверки
📌 Cognitive complexity снизилась, временная сложность — тоже. Производительность выросла.
⚠️ Проблема №3: код, который не нужен, но работает
Профайлинг показал, что куча ресурсов уходит на код, который вообще не должен уже как год использоваться. Но код активно выполняется. WTF!?
🤷♂️ Функции вызываются, результат — пустой, но код исполняется. Причём часто и тяжело.
✅ Решение:
📌 Удалил мёртвый код, обновил импорты, подчистил зависимости.
📌 Поднял вопрос о полной деактивации этого кода и мы таки это сделали — ресурсы можно использовать лучше.
🐘 Проблема №4: Импорты. Много. Дорого.
Во время анализа я наткнулся на ещё одну тихую, но дорогую проблему — огромные ресурсы уходят на фазу импорта модулей в Python.
Конкретно — на
_find_and_load
, часть механизма импорта, который занимается поиском, загрузкой и инициализацией модулей.📌 Почему это важно?
- Импорты выполняются на каждый старт сервиса.
- Чем жирнее и зависимее ваши модули, тем дольше и тяжелее проходит импорт.
- Это не всегда очевидно, но можно видеть в профайлинге:
_find_and_load
, _find_and_load_unlocked
, _load_unlocked
– вот это всё.📊 У нас в сервисе модулей реально много.
Многое из них – просто "свалены в кучу", где-то грузятся тяжёлые зависимости. Да и сложная структура проекта вынуждает иметь большое количество импортов.
И, как итог, CPU тратится на то, что можно было бы стремиться избежать.
✅ Что с этим делать:
- Разделять модули по функциональности.
- Отложенные импорты (lazy import) – практика может применяться (но есть нюансы), если модуль нужен только в конкретной функции.
- Минимизировать зависимости и импорт только того, что действительно нужно.
- Следить за импортами в
__init__.py
— именно они могут тянуть за собой пол кодовой базы.Следующий шаг: memory профайлинг. Pyroscope в нашей инфрастуктуре такое не умеет и нужно приседать, но надеюсь дойдут руки и до RAM.
👏12🤡5👍2❤1
Forwarded from Таксики и лытдыбр σποραδικος
Принесла вам чудесную серию рисунков — Franciszka Themerson, Science and Technology:
1. radio-astronomy
2. distillery
3. high voltage
и, внезапно
4. the tea break (британские ученые такие британские, даже если они польские художницы, перебравшиеся в Лондон в сороковом)
1. radio-astronomy
2. distillery
3. high voltage
и, внезапно
4. the tea break (британские ученые такие британские, даже если они польские художницы, перебравшиеся в Лондон в сороковом)
👍4❤3👎1
#prog #article #amazingopensource
Jujutsu (jj) — система контроля версий, которая концептуально проще git и при этом мощнее.
Неплохой (но местами устаревший) обзор Jujutsu — jj init — сделал Chris Krycho. Также есть пока что неполный туториал от Стива Клабника, который тот написал главным образом для того, чтобы самому лучше разобраться с Jujutsu. Этот туториал даже рекомендуется в официальной документации.
Сразу скажу: jj работает поверх git, так что её можно поставить поверх имеющегося репозитория и потом без проблем удалить. (у jujutsu также есть нативный бекенд, но он пока не готов и не рекомендуется к использованию)
Что же такого примечательного в этой VCS? Попробую объяснить (но лучше почитайте по ссылкам, чесслово, там хорошо описано). В обоих описаниях бросается в глаза наличие операций, описанных в Where are my Git UI features from the future?.
Как и git, jj идейно работает на снапшотах файловой системы. В отличие от git, коммиты в jj напоминают объекты в ООП: они мутабельны и имеют идентичность, которая остаётся постоянной при изменениях самих коммитов — да, в том числе при ребейзах.
В отличие от git, в jj нет отдельной стадии стейджинга изменений и фиксации коммита. Более того, jj вообще не использует индекс — jj отслеживает изменения файлов и автоматически записывает их в текущий коммит (не волнуйтесь, есть средства для разбиения изменений). Для того, чтобы закончить с текущим коммитом, достаточно вызвать
Как я уже сказал, описание коммитов можно добавлять и менять в любой момент, вне зависимости от того, какой коммит текущий.
Ветки в jj анонимны, и это взрывает мозг тем, кто учился VCS на git (мне в том числе). Именованные ветки из git доступны в jj под именем bookmarks, и, в отличие от git, эти указатели не смещаются автоматически при добавлении дочерних коммитов — для этого нужно вызвать отдельную команду. Это выглядит неудобным, но по факту это даёт некоторые преимущества. Одно из них — нет нужды придумывать имя для каждого логического изменения. Оно требуется лишь тогда, как нужно расшарить изменения с другими. Другое — если в локальной копии слишком много изменений и стало понятно, что они идут куда-то не туда, можно просто откатиться до ветки (при условии, что локально её не двигали) и просто дропнуть коммиты после неё.
Коммиты можно свободно двигать в истории. В отличие от git rebase, имена коммитов после этого не меняются.
Ребейз и мердж в jj всегда успешно завершаются. Это не означает, что конфликты невозможны. Но в jj конфликты являются первоклассными значениями. Это позволяет слить несколько последовательностей коммитов в одну и разобраться с конфликтами позднее (и потом, возможно, слить разрешения конфликтов с мердж-коммитами).
Команда
Почти все команды jj позволяют указывать коммиты, на которых они действуют (и используют текущий коммит, если этот аргумент не указан). Более того, это может быть несколько коммитов — или revset в терминологии jj. Задавать revset-ы можно при помощи отдельного достаточно мощного языка выражений. В качестве побочного эффекта это означает, что, помимо всего прочего, в jj можно сделать мердж больше двух веток сразу.
Например, для того, чтобы просмотреть все локальные "ветки" (коммиты без дочерних коммитов), можно использовать
Jujutsu (jj) — система контроля версий, которая концептуально проще git и при этом мощнее.
Неплохой (но местами устаревший) обзор Jujutsu — jj init — сделал Chris Krycho. Также есть пока что неполный туториал от Стива Клабника, который тот написал главным образом для того, чтобы самому лучше разобраться с Jujutsu. Этот туториал даже рекомендуется в официальной документации.
Сразу скажу: jj работает поверх git, так что её можно поставить поверх имеющегося репозитория и потом без проблем удалить. (у jujutsu также есть нативный бекенд, но он пока не готов и не рекомендуется к использованию)
Что же такого примечательного в этой VCS? Попробую объяснить (но лучше почитайте по ссылкам, чесслово, там хорошо описано). В обоих описаниях бросается в глаза наличие операций, описанных в Where are my Git UI features from the future?.
Как и git, jj идейно работает на снапшотах файловой системы. В отличие от git, коммиты в jj напоминают объекты в ООП: они мутабельны и имеют идентичность, которая остаётся постоянной при изменениях самих коммитов — да, в том числе при ребейзах.
В отличие от git, в jj нет отдельной стадии стейджинга изменений и фиксации коммита. Более того, jj вообще не использует индекс — jj отслеживает изменения файлов и автоматически записывает их в текущий коммит (не волнуйтесь, есть средства для разбиения изменений). Для того, чтобы закончить с текущим коммитом, достаточно вызвать
jj new
для создания нового коммита, который отпочковывается от текущего. Описание для этого вводить необязательно — его можно добавить задним числом. Недостаток этого решения — большинство инструментов поверх git предполагают использование индекса и потому не очень хорошо стыкуются с jj.Как я уже сказал, описание коммитов можно добавлять и менять в любой момент, вне зависимости от того, какой коммит текущий.
Ветки в jj анонимны, и это взрывает мозг тем, кто учился VCS на git (мне в том числе). Именованные ветки из git доступны в jj под именем bookmarks, и, в отличие от git, эти указатели не смещаются автоматически при добавлении дочерних коммитов — для этого нужно вызвать отдельную команду. Это выглядит неудобным, но по факту это даёт некоторые преимущества. Одно из них — нет нужды придумывать имя для каждого логического изменения. Оно требуется лишь тогда, как нужно расшарить изменения с другими. Другое — если в локальной копии слишком много изменений и стало понятно, что они идут куда-то не туда, можно просто откатиться до ветки (при условии, что локально её не двигали) и просто дропнуть коммиты после неё.
Коммиты можно свободно двигать в истории. В отличие от git rebase, имена коммитов после этого не меняются.
Ребейз и мердж в jj всегда успешно завершаются. Это не означает, что конфликты невозможны. Но в jj конфликты являются первоклассными значениями. Это позволяет слить несколько последовательностей коммитов в одну и разобраться с конфликтами позднее (и потом, возможно, слить разрешения конфликтов с мердж-коммитами).
Команда
jj undo
отменяет действие предыдущей команды. Дико не хватает этого в git.Почти все команды jj позволяют указывать коммиты, на которых они действуют (и используют текущий коммит, если этот аргумент не указан). Более того, это может быть несколько коммитов — или revset в терминологии jj. Задавать revset-ы можно при помощи отдельного достаточно мощного языка выражений. В качестве побочного эффекта это означает, что, помимо всего прочего, в jj можно сделать мердж больше двух веток сразу.
Например, для того, чтобы просмотреть все локальные "ветки" (коммиты без дочерних коммитов), можно использовать
jj log -r 'heads(all())'
. all()
возвращает все коммиты в репе, а heads
извлекает из набора коммитов те, у которых нет дочерних. Если этого недостаточно и нужно добавить контекста — скажем, по два родительских коммита — можно использовать jj log -r 'ancestors(heads(all()), 2)'
. (это лишь мой пример, по умолчанию jj log
использует несколько более полезный формат)👍14🤔4
"Пусть всё пройдёт, как по маслу"
А ничего, что масло вообще-то довольно плохая смазка?
А ничего, что масло вообще-то довольно плохая смазка?
🤡11💩5🤔4😐4😁3💯1
Forwarded from Евгений Касперский
Telegram
Kaspersky
⚡️Зиродей в Chrome: один клик – и все данные под угрозой
Эксперты Kaspersky GReAT обнаружили уязвимость нулевого дня в Google Chrome, которая позволяла атакующим обходить защиту браузера и заражать устройства буквально одним кликом. Всё, что нужно было жертве…
Эксперты Kaspersky GReAT обнаружили уязвимость нулевого дня в Google Chrome, которая позволяла атакующим обходить защиту браузера и заражать устройства буквально одним кликом. Всё, что нужно было жертве…
Почему надо обновлять не только Chromium.
Прямо вслед за новостью об уязвимости в Chrome и Chromium, свой браузер Firefox пропатчила Mozilla.
Да, Хромиума нет, а уязвимость, получается, есть (была), и она находилась в логике взаимодействия песочницы с ОС, которая у разных браузеров имеет (имела) схожую ошибку.
Напомню, мы недавно обнаружили дыру в Chrome и лежащем в его основе Chromium ( который также используется в Яндекс.Браузере, MS Edge и т.д.).
Уязвимость использовали в реальных атаках на цели в России, судя по всему, достаточно продвинутые хакеры.
В общем, обновляйте и Firefox тоже.
Прямо вслед за новостью об уязвимости в Chrome и Chromium, свой браузер Firefox пропатчила Mozilla.
Да, Хромиума нет, а уязвимость, получается, есть (была), и она находилась в логике взаимодействия песочницы с ОС, которая у разных браузеров имеет (имела) схожую ошибку.
Напомню, мы недавно обнаружили дыру в Chrome и лежащем в его основе Chromium ( который также используется в Яндекс.Браузере, MS Edge и т.д.).
Уязвимость использовали в реальных атаках на цели в России, судя по всему, достаточно продвинутые хакеры.
В общем, обновляйте и Firefox тоже.
👍4
Что ж
Номинально мне нужно ждать ещё четыре часа, но календарно —
С днём рождения меня, с днём рождения меня.
27 лет. Пора в клуб.
Номинально мне нужно ждать ещё четыре часа, но календарно —
С днём рождения меня, с днём рождения меня.
27 лет. Пора в клуб.
🎉39❤🔥5🤡4
Админ
@test_channel_grammers
, не пересылай все посты Блог*а подряд, пожалуйста🤡2👍1👎1😢1
Forwarded from Toil Thoughts
Несколько недель назад на GitHub, наконец-то, появилась возможность отсоединить свои публичные форки без потери всех метаданных (issues, stars, releases, etc) репозитория.
Работает примерно так же, как и на Gitlab — нажимаешь на кнопку "отсоединить" в настройках репозитория, и все готово. Единственное отличие, что если репозиторий форка слишком большой (1 GB+ или имеет другие дочерние форки), то для отсоединения все-таки нужно будет написать в саппорт и объяснить причину отсоединения.
Я давно хотел преобразовать один из своих форков в самостоятельный репозиторий, поэтому не мог пройти мимо и не проверить перенос через саппорт лично. На удивление, мне довольно быстренько ответили и отсоединили форк без лишних вопросов. Теперь репозиторий отображается в поиске на GitHub, а в моем профиле стала показываться даже вся старая статистика коммитов из репозитория🐈⬛
Работает примерно так же, как и на Gitlab — нажимаешь на кнопку "отсоединить" в настройках репозитория, и все готово. Единственное отличие, что если репозиторий форка слишком большой (1 GB+ или имеет другие дочерние форки), то для отсоединения все-таки нужно будет написать в саппорт и объяснить причину отсоединения.
Я давно хотел преобразовать один из своих форков в самостоятельный репозиторий, поэтому не мог пройти мимо и не проверить перенос через саппорт лично. На удивление, мне довольно быстренько ответили и отсоединили форк без лишних вопросов. Теперь репозиторий отображается в поиске на GitHub, а в моем профиле стала показываться даже вся старая статистика коммитов из репозитория
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥2🤡2
#rust
Add panic_unreachable_unchecked feature flag to the standard library
Add panic_unreachable_unchecked feature flag to the standard library
This is similar to panic_immediate_abort except that all panics are considered immediate UB and can therefore be optimized away as unreachable by the compiler.
This has been tested on regalloc3 where it resulted in a 10% speedup compared to using a normal standard library, mainly due to the elimination of bounds checks.
While it may seem that this feature merely to satisfy those with a reckless thirst for performance at any cost, it is also useful for saner heads as a profiling tool to investigate the impact of unnecessary safety check and find places where unsafe code could be used to avoid them.
❤🔥9🤡3👍1