<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <language>ru</language>
    <title>Оптимизация</title>
    <description>Статьи, которые помогут вам найти «бутылочные горлышки» в вашем коде и оптимизировать работу вашего проекта в целом.</description>
    <link>https://tproger.ru/tag/optimization</link>
    <atom:link href="https://tproger.ru/tag/optimization/feed" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Sat, 17 Oct 2026 18:18:20 GMT</lastBuildDate>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>12</sy:updateFrequency>
    <image>
      <title>Оптимизация</title>
      <link>https://tproger.ru</link>
      <url>https://tproger.ru/apple-touch-icon.png</url>
    </image>
    <item>
      <title>Что такое токены и как они влияют на стоимость запросов к нейросети</title>
      <link>https://tproger.ru/articles/chto-takoe-tokeny-i-kak-oni-vliyayut-na-stoimost-zaprosov-k-nejros</link>
      <comments>https://tproger.ru/articles/chto-takoe-tokeny-i-kak-oni-vliyayut-na-stoimost-zaprosov-k-nejros?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/chto-takoe-tokeny-i-kak-oni-vliyayut-na-stoimost-zaprosov-k-nejros</guid>
      <description><![CDATA[<p>Что такое токены в нейросетях, как считаются входные и выходные токены, от чего зависит стоимость API-запросов и как снизить расходы с помощью оптимизации контекста и кэширования.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/chto-takoe-tokeny-i-kak-oni-vliyayut-na-stoimost-zaprosov-k-nejros">Что такое токены и как они влияют на стоимость запросов к нейросети</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 05 Oct 2026 11:34:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Вы отправляете модели лог ошибки и просите объяснить, что сломалось. Вопрос занимает одну строку, ответ — два абзаца, а счётчик показывает тысячи токенов. Откуда они взялись? В расход попадает весь переданный модели контекст, включая сам лог.</p><h2>Что такое токен простыми словами</h2><p>Текст, прежде чем попасть в языковую модель, разбивается на токены, небольшие кусочки, и каждый может оказаться целым словом, обрывком слова или просто знаком препинания. Как именно текст режут на токены, определяет токенизатор — отдельная программа со своим словарём. Она разбивает текст на фрагменты и каждому фрагменту, то есть токену, присваивает число — идентификатор из этого словаря.</p><p>Этот словарь работает примерно как поварская книга, только наоборот: рецепт показывает, из каких ингредиентов собрано блюдо, а словарь токенизатора, позволяет разбирать уже готовый текст на такие же кусочки-ингредиенты и каждому присваивать свой номер. Так суп из слов и знаков препинания превращается в строку чисел, где каждое число обозначает пронумерованный кусочек текста той или иной длины. Как именно происходит такое разбиение на токены и их нумерация, показано в курсе <a href="https://huggingface.co/learn/llm-course/en/chapter2/4">Hugging Face</a>.</p><p>Представьте, что вы измеряете длину комнаты шагами. При широком шаге получится одно число, при коротком — другое. Само число шагов ещё не говорит, сколько метров вы прошли. С токенами похожая история: сто токенов нельзя однозначно перевести в сто слов или фиксированное число символов.</p><p>У этой аналогии есть дополнительное условие: внутри одного текста длина «шага» тоже меняется. Один токен захватывает слово целиком, соседний — лишь несколько букв. Границы задаёт токенизатор по своим правилам, а не модель: на вход она получает уже готовую последовательность токенов.</p><p>Когда модель составляет ответ, она <a href="https://huggingface.co/docs/transformers/main/en/llm_tutorial">выбирает</a> следующие токены с учётом уже имеющейся последовательности, а программа преобразует их обратно в читаемый текст. Токен при этом не равен одной законченной мысли или фиксированному времени вычислений — метафора шагов описывает только размер фрагментов, не саму работу нейросети.</p><h2>Почему один токен не равен одному слову</h2><p>В <a href="https://huggingface.co/docs/transformers/v4.38.0/tokenizer_summary#subword-tokenization">документации</a> Hugging Face есть пример для токенизатора модели BERT с идентификатором google-bert/bert-base-uncased. Он разбивает фразу из пяти слов так:</p><p>I have a new GPU!<br />["i", "have", "a", "new", "gp", "##u", "!"]</p><p>Получилось семь токенов. Слова I, have, a и new заняли по одному. Сокращение GPU распалось на два, а восклицательный знак стал отдельным токеном. Обозначение ## показывает, что фрагмент продолжает предыдущее слово. Этот токенизатор также переводит буквы в нижний регистр.</p><p>Это конкретный пример разбиения, а не универсальное правило для всех нейросетей. У другой модели может быть иной словарь, поэтому та же фраза даст другую последовательность. В некоторых схемах токенизации пробел входит в токен вместе с соседним текстом; отдельный символ, например эмодзи, тоже может потребовать нескольких токенов.</p><p>Частые сочетания символов позволяют представить текст крупными фрагментами. Для редкого имени или длинного идентификатора в коде токенизатору может понадобиться больше коротких фрагментов. Поэтому количество символов не даёт точного количества токенов.</p><p>Язык тоже влияет на разбиение. Исследователи <a href="https://arxiv.org/abs/2305.13707">сравнили</a> токенизацию текстов на разных языках и показали, что одинаковое содержание может требовать разного числа токенов. Однако правило вроде «русский всегда вдвое дороже английского» из этого не следует — результат зависит от конкретной модели и текста.</p><h2>Какие токены попадают в запрос и ответ</h2><p>В API обычно разделяют входные и выходные токены. Входные описывают то, что модель получает для выполнения задачи. Выходные относятся к тому, что она генерирует.</p><p>К входу относится гораздо больше, чем последняя реплика пользователя. Например, <a href="https://ai.google.dev/gemini-api/docs/tokens">документация</a> Gemini включает в подсчёт системные инструкции, историю разговора и описание доступных инструментов. В приложении запрос может содержать такие данные:</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-10-05/e8536990-0a61-4127-9ab8-aaaec9c46803.webp" alt="" /></figure><p>Поэтому короткая реплика «А теперь исправь» может сопровождаться большим входом: приложение снова передаёт код, переписку и инструкции. Предыдущий ответ модели, включённый в следующий запрос, уже относится к входным токенам этого запроса.</p><p>У моделей с режимом рассуждений расход может включать токены, которые не совпадают с видимым пользователю ответом. Например, Anthropic <a href="https://platform.claude.com/docs/en/build-with-claude/context-windows#the-context-window-with-thinking">учитывает токены рассуждений</a> в выходном бюджете и тарификации. Поэтому длина текста на экране не всегда объясняет весь расход.</p><h2>Как токены ограничивают размер контекста</h2><p>Контекстное окно — это объём текста, с которым модель работает одновременно, пока готовит ответ. В стандартном случае в него входят переданный запрос и генерируемое продолжение. Это ограничение описано в <a href="https://platform.claude.com/docs/en/build-with-claude/context-windows">документации</a> Claude. Оно относится к текущему обращению, а не ко всем знаниям, которые модель получила при обучении.</p><p>Для наглядности возьмём условную модель с общим окном в 32 000 токенов. Если инструкции, переписка и документы уже заняли 28 000, в пределах этого окна остаётся максимум 4 000 на генерацию. Дополнительно у модели может быть собственный предел длины ответа. Большое контекстное окно само по себе не обещает ответ такого же размера.</p><p>При переполнении поведение зависит от API и приложения: запрос могут отклонить, генерация может остановиться, а чат-клиент может заранее сократить историю. Нельзя рассчитывать, что модель сама выберет, какие старые сообщения можно безболезненно забыть.</p><p>Если вы отправляете большой документ, сначала оцените весь вход и оставьте место для ответа. Для бота с длинными диалогами определите, какие сообщения он передаёт дальше, какие заменяет кратким резюме и какие детали обязан сохранять дословно. Anthropic <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents">предупреждает</a> о потере важных деталей при слишком сильном сжатии истории. Например, резюме обсуждения может сохранить решение команды, но потерять строку ошибки, без которой модель не воспроизведёт проблему.</p><h2>Как посчитать стоимость запроса</h2><p>В тарифах API обычно отдельно указаны цены входных и выходных токенов, часто за миллион. Число «1 млн» здесь обозначает единицу расчёта. Оно не означает, что для одного короткого запроса нужно израсходовать миллион токенов.</p><p>Для обычного текстового запроса без кэширования и дополнительных платных операций расчёт выглядит так:</p><p>Стоимость запроса =<br />    входные токены / 1 000 000 × цена входа<br />  + выходные токены / 1 000 000 × цена выхода</p><p>Посмотрим на конкретный тариф. На 28 сентября 2026 года в <a href="https://routerai.ru/models/deepseek/deepseek-v4-pro-0813">карточке deepseek/deepseek-v4-pro-0813 на RouterAI</a> для провайдера Ionstream указаны 34 ₽ за миллион входных и 300 ₽ за миллион выходных токенов. У других провайдеров этой же модели цены могут отличаться, а рублёвые значения меняются вместе с курсом валют.</p><p>Допустим, запрос использовал 2 000 входных и 500 оплачиваемых выходных токенов. Это учебный объём для расчёта, а не результат тестирования модели. Без кэширования получится:</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-10-05/5cbf062b-9959-4c34-8ee4-8644079a6e8d.webp" alt="" /></figure><p>При этих условиях короткий ответ стоит больше, чем вчетверо более длинный вход. Поэтому для сравнения моделей нужны обе ставки и характерная для вашей задачи длина ответа. Сравнение только по цене входа может привести к неверному выбору.</p><p>Кэширование промпта позволяет провайдеру повторно использовать уже обработанную общую часть запросов. Это меняет расчёт стоимости: например, Anthropic <a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching">применяет разные ставки</a> к записи и чтению кэша. Повторённый текст при этом продолжает занимать место в контекстном окне. Поддержку и условия кэширования нужно проверять у выбранной модели и провайдера.</p><p>Для практического сравнения можно использовать <a href="https://routerai.ru/">RouterAI</a>: сервис даёт доступ к моделям разных разработчиков через единый API-ключ, работает без VPN и принимает оплату в рублях российскими картами. В <a href="https://routerai.ru/models">каталоге моделей</a> можно посмотреть ставки на вход и выход и сравнить расход на одинаковых задачах — токенизация и тарифы при этом остаются собственными у каждой модели.</p><h2>Как проверить, что кэш действительно работает</h2><p>Пометить запрос как кэшируемый мало. Провайдер применит кэш, только если повторяющаяся часть образует одинаковое начало запроса: кэш привязан к префиксу и хранит внутреннее состояние модели для конкретного начала текста, а не для содержимого в целом — это устройство кэша <a href="https://routerai.ru/pages/prompt-caching">описано</a> на RouterAI.</p><p>Отсюда частая ошибка компоновки запроса. Если статические инструкции лежат в системном блоке, а большой неизменный документ — в сообщении пользователя после переменных данных, кэш формально настроен, но фактически не срабатывает: перед неизменным текстом каждый раз оказывается разное начало.</p><p>Проверяется это не по настройкам, а по ответу API — в статистике должно расти поле чтения кэша. У Claude это cache_read_input_tokens и cache_creation_input_tokens в объекте usage, как описано в <a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching">документации</a> Anthropic; у других провайдеров поля называются иначе, но смысл тот же. Если это поле стабильно нулевое при повторяющихся запросах, кэш не работает, и вы платите полную цену за один и тот же текст на каждом вызове.</p><p>Отдельная ловушка — дописывать что-либо к системной части между вызовами. Одна добавленная фраза меняет начало запроса, и кэш промахивается. Особенно чувствительно это в автоматизированных циклах переделок, где один и тот же запрос уходит по несколько раз подряд: там промах по кэшу не просто снижает экономию, а умножает полную стоимость на число повторов.</p><h2>Как узнать расход своего запроса</h2><p>До отправки используйте токенизатор выбранной модели или официальный метод подсчёта, если он доступен. Считать нужно полный запрос с инструкциями и историей. Например, <a href="https://platform.claude.com/docs/en/build-with-claude/token-counting">метод подсчёта</a> токенов Claude принимает структурированные сообщения и возвращает оценку входа. Документация отдельно предупреждает: оценка может немного отличаться от фактического расхода.</p><p>После ответа проверяйте статистику API. В <a href="https://routerai.ru/docs/guides/overview/multimodal/pdfs#%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82-%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D0%B0">примере</a> формата ответа RouterAI для этого используется объект usage. Для учебного объёма из расчёта выше его базовые поля выглядели бы так:</p><p>Поле prompt_tokens показывает вход, completion_tokens — выход, total_tokens — их сумму. Для кэширования и рассуждений проверяйте дополнительную детализацию, которую возвращает конкретный провайдер. Умножать total_tokens на одну ставку нельзя, если вход и выход стоят по-разному.</p><p>Сохраняйте вместе со счётчиками идентификатор модели и фактически использованного провайдера. Так вы сможете объяснить изменение расходов: выросла история диалога, модель стала отвечать подробнее или запрос обработал провайдер с другим тарифом.</p><p>У систем, где ИИ работает автоматически, есть источник роста расходов, не связанный с самой моделью, — обвязка вокруг неё. Значимый патч CRM, воркфлоу или промпта может незаметно сломать защиту от лишних вызовов: настройку кэша, лимит на число повторов, дедупликацию похожих запросов. Тесты при этом пройдут — патч не ломает функциональность, он ломает экономику. Поэтому после каждого крупного изменения в такой системе стоит прогнать её типовой сценарий и сверить счётчики usage с тем, что было до патча, а не считать расход токенов побочным эффектом, который не требует отдельной проверки.</p><h2>Как сократить расход без потери нужного контекста</h2><p>Начните с того, что приложение отправляет и что просит вернуть. Обычно именно здесь можно найти данные, которые не помогают решить текущую задачу.</p><ul><li>Передавайте нужный фрагмент материала. Для объяснения одной ошибки приложите связанный с ней код и достаточное окружение. Весь репозиторий может содержать много файлов, которые не влияют на проблему.</li><li>Управляйте историей диалога. Не передавайте её целиком в каждый новый запрос — резюмируйте закрытые темы и оставляйте дословно только то, что ещё понадобится.</li><li>Задавайте формат результата. Если приложению нужны название ошибки и способ исправления, перечислите эти поля. Тогда у модели будет меньше причин добавлять длинное вступление и общий обзор темы.</li><li>Ограничивайте генерацию параметром API. В RouterAI для этого <a href="https://routerai.ru/docs/guides/overview/parameters" rel="nofollow">описаны</a> max_tokens и max_completion_tokens. Выбирайте параметр, который поддерживает ваша модель. Слишком низкий предел может оборвать полезный ответ; сам предел не обязывает модель заполнить его целиком.</li><li>Проверяйте результат сокращений. Прогоните изменённый запрос на характерных задачах и сравните расход вместе с качеством ответа. Если после удаления контекста приходится повторять запрос с уточнениями, экономия может исчезнуть.</li><li>Проверяйте кэш по факту, а не по настройке. Включённый в коде кэш ничего не гарантирует — ориентируйтесь на поле чтения кэша в статистике запроса.</li></ul><p>Для первого замера возьмите одну задачу, которую ваш бот решает регулярно. Зафиксируйте вход, выход и стоимость, затем уберите из запроса один лишний блок и повторите проверку. Так станет видно, какой именно фрагмент увеличивал счёт и сохранила ли модель всё необходимое для правильного ответа.</p>]]></content:encoded>
    </item>
    <item>
      <title>Оптимизация изображений для веба: где теряются мегабайты</title>
      <link>https://tproger.ru/articles/optimizaciya-kartinok-dlya-veba-gde-teryayutsya-megabajty-i-kak-ih-v</link>
      <comments>https://tproger.ru/articles/optimizaciya-kartinok-dlya-veba-gde-teryayutsya-megabajty-i-kak-ih-v?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/optimizaciya-kartinok-dlya-veba-gde-teryayutsya-megabajty-i-kak-ih-v</guid>
      <description><![CDATA[<p>Оптимизация изображений для веба: размер в пикселях, формат под тип картинки, AVIF против WebP и сжатие в браузере. Проверьте свои картинки по чеклисту.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/optimizaciya-kartinok-dlya-veba-gde-teryayutsya-megabajty-i-kak-ih-v">Оптимизация изображений для веба: где теряются мегабайты</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Алгоритмы сжатия]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 07 Sep 2026 09:00:41 GMT</pubDate>
      <content:encoded><![CDATA[<p>Картинки обычно оказываются самой тяжёлой частью страницы, и почти всегда они тяжелее, чем нужно. Скриншот интерфейса на 2,1 МБ ужимается до 102 КБ без заметной глазу разницы, а фотография на 4,7 МБ теряет три четверти веса ещё до того, как вы дотронетесь до качества сжатия.</p><p>Разница между четырёхсекундной загрузкой и загрузкой меньше секунды обычно лежит именно здесь, а не в бандле скриптов. Ниже пошаговая оптимизация изображений: что делать первым, какой формат выбирать под какой тип картинки и почему кодировать современные форматы нужно на сборке, а не на лету.</p><p>Первым делом уменьшайте размер в пикселях, а не качество: фотография 6000×4000 в карточке шириной 1200 пикселей это чистые потери. Один этот шаг убрал 77% веса тестового снимка.</p><p>Формат выбирается под тип картинки: фотографиям WebP или AVIF, скриншотам и интерфейсам сжатие без потерь, логотипам SVG, анимациям анимированный WebP вместо GIF.</p><p>AVIF при равном качестве весит вдвое меньше JPEG, но кодируется примерно в пятнадцать раз дольше, поэтому генерировать его нужно на этапе сборки.</p><p>Метаданные съёмки занимают от 10 до 30 КБ на файл и в вебе не нужны ни для чего.</p><p>Сжатие с потерями на тексте и резких границах даёт видимые артефакты: скриншоты и логотипы жмутся только без потерь, зато на 20–60%.</p><h2>Шаг, который экономит больше всех остальных</h2><p>Прежде чем подбирать кодек и качество, посмотрите на фактические размеры. Фотография 6000×4000 в карточке шириной 1200 CSS-пикселей отдаёт браузеру в двадцать пять раз больше пикселей, чем нужно обычному экрану, и вшестеро больше, чем нужно экрану с двойной плотностью.</p><p>В замерах автора чеклиста <a href="https://dev.to/_544bf5cbd223c35a49756/image-optimization-the-complete-2026-checklist-for-faster-websites-3pi5">одно только уменьшение до реально отображаемого размера ужало тестовое изображение с 4,7 МБ до 1,1 МБ</a>. Это минус 77% при нулевой потере качества: пиксели, которых не видно, просто перестали передаваться.</p><p><b>Как определить нужный размер:</b><br />Отправная точка это ширина контейнера в CSS-пикселях. На обычном экране она же и есть нужная ширина в физических пикселях, на плотных экранах нужно кратно больше: у части ноутбуков это двойка, у большинства современных смартфонов тройка. Поэтому правильный инструмент здесь не фиксированный множитель, а атрибут srcset с несколькими вариантами файла: браузер сам возьмёт тот, что соответствует плотности экрана посетителя.</p><h2>Формат под тип изображения, а не один на всё</h2><p>Самая частая ошибка — выбрать один формат и применять его ко всему подряд. Между фотографией с плавными градиентами и скриншотом с текстом и резкими границами разница принципиальная, и сжимаются они противоположными способами.</p><ul><li>Фотографии — WebP (на 30–50% меньше JPEG) или AVIF (ещё примерно на 30% меньше WebP).</li><li>Скриншоты и элементы интерфейса — PNG или WebP без потерь.</li><li>Логотипы и иконки — SVG: вектор весит копейки и масштабируется без предела.</li><li>Анимации — анимированный WebP вместо GIF, это экономит 60–80% веса.</li></ul><p>Отдельно про текст. Сжатие с потерями на буквах и резких границах создаёт заметные ореолы вокруг символов, поэтому скриншоты и логотипы обрабатываются только алгоритмами без потерь. Инструменты вроде oxipng ужимают такие файлы на 20–60%, не меняя при этом ни одного пикселя.</p><h3>Конвертация и сжатие — это два разных действия</h3><p>Перевод PNG в JPEG экономит место. Сжатие получившегося JPEG экономит ещё столько же, и про этот второй проход регулярно забывают. Для фотографий рабочий диапазон качества это 75–82%: от стопроцентного такая картинка визуально неотличима, а весит заметно меньше.</p><h3>Метаданные, которые вы отдаёте бесплатно</h3><p>В файле с камеры или из редактора лежат EXIF, координаты GPS, модель камеры и метки программы обработки. Для показа в браузере это бесполезный груз весом от 10 до 30 КБ на файл, за одним исключением: тег ориентации. Часть снимков с телефона хранит пиксели неповёрнутыми и полагается на него, поэтому поворот нужно применить к самим пикселям до того, как метаданные срежутся, иначе фотографии лягут набок. На каталоге из тысячи товарных снимков набегает до 30 МБ трафика ни за что, а координаты съёмки вдобавок могут оказаться данными, которые вы не собирались публиковать.</p><h2>AVIF: что это и почему его нельзя кодировать на лету</h2><p>AVIF (AV1 Image File Format) выпущен Alliance for Open Media в 2019 году и построен на внутрикадровом кодировании видеокодека AV1. По сути это инструменты сжатия одного кадра AV1, упакованные в самостоятельный контейнер для картинок.</p><p>Перед WebP у него два практических преимущества: выше степень сжатия при равном качестве и глубина цвета до 12 бит с поддержкой расширенного динамического диапазона, тогда как WebP ограничен восемью битами и обычным диапазоном. Достигается это за счёт заметно более богатого набора инструментов предсказания, унаследованного от видеокодека.</p><h3>Сколько это в килобайтах</h3><p>В сравнительном тесте <a href="https://dev.to/uglypeardata/avif-format-guide-the-next-generation-image-compression-std-4oa5">брали пейзажный кадр 1920×1080 с градиентами неба, текстурой листвы и границами зданий, исходник в BMP весил 5,93 МБ</a>. Целевое качество задавали по метрике структурного сходства: она сравнивает сжатую картинку с исходной, где единица означает полное совпадение, а 0,92 примерно соответствует порогу, за которым разницу перестают замечать глазом. Результаты:</p><p>На листве и градиентах неба разница видна и глазом: JPEG на низком битрейте показывает цветовые блоки, у AVIF артефактов практически нет. Отвечают за это многоуровневые фильтры внутри цикла кодирования, которые заглаживают границы блоков и восстанавливают детали текстур.</p><p>Плата за это — время. Кодирование AVIF примерно в пятнадцать раз дольше JPEG и вчетверо дольше WebP, потому что выбор разбиения и типа преобразования требует перебора с оптимизацией. Зато декодирование медленнее JPEG всего вдвое, и на просмотр это почти не влияет. Практический вывод прямой: генерируйте AVIF заранее, на этапе сборки. Сервисы доставки изображений кодируют его и по запросу, но только с кешированием результата, чтобы платить за кодирование один раз на вариант, а не на каждого посетителя. Чего делать нельзя, так это кодировать заново при каждом обращении.</p><h3>Как отдавать современный формат и не потерять старые браузеры</h3><p>Поддержка AVIF в браузерах составляет примерно 96%, но подстраховка всё равно нужна. Делается она тегом &lt;picture&gt;: браузер берёт первый формат, который понимает, и до остальных не доходит.</p><p>Атрибуты width и height здесь не декоративные: без них браузер не знает пропорций до загрузки файла и подвёрстывает страницу заново, когда картинка приходит. Это тот самый прыжок содержимого, который портит метрику визуальной стабильности. Атрибут loading=lazy убирает изображения за пределами первого экрана из очереди, конкурирующей за отрисовку главного элемента.</p><p>Именно за пределами: на картинку первого экрана его ставить нельзя. Отложенная загрузка задержит ровно тот элемент, скорость появления которого и меряет метрика отрисовки основного содержимого. Там уместен противоположный по смыслу fetchpriority=high.</p><h2>Сжатие в браузере: почему это вообще возможно</h2><p>Браузер умеет декодировать, преобразовывать и кодировать изображение прямо на устройстве, не отправляя файл никуда. Для товарных снимков, клиентских материалов и прочего чувствительного это снимает целый шаг из конвейера вместе с вопросом, где полежит копия.</p><p>Речь здесь уже не про ассеты вашего сайта, которые готовятся на сборке. Речь про картинки, которые в браузер приносит сам пользователь: форма загрузки в личном кабинете, объявление на маркетплейсе, вложение в заявку. Сжать их до отправки на сервер можно прямо во вкладке, и это снимает и трафик, и вопрос о том, где полежит оригинал.</p><p>Автор одного из таких инструментов <a href="https://dev.to/_544bf5cbd223c35a49756/how-i-built-a-browser-image-compressor-that-handles-30-images-at-once-56k4">разобрал устройство своего конвейера</a>, и его опыт пригодится всем, кто выносит тяжёлые вычисления во фронтенд.</p><h3>Вся тяжёлая работа уходит из главного потока</h3><p>Перекодирование снимка 4000×4000 нагружает процессор настолько, что в главном потоке интерфейс замирает, а браузер показывает предупреждение о зависшей странице. Поэтому весь конвейер живёт в Web Worker: главный поток отвечает за выбор файлов, превью и состояние, а рабочий поток декодирует через createImageBitmap, применяет преобразования и кодирует через OffscreenCanvas. Именно через него, а не через canvas.toBlob: последний это метод DOM-элемента, а DOM в рабочем потоке отсутствует, и нужный метод там называется convertToBlob.</p><p>Важная деталь производительности: передавать данные между потоками нужно как ArrayBuffer через механизм передачи владения, а не структурным клонированием. На пачке из тридцати изображений это разница между мгновенным откликом и лишними полусекундами нагрузки на сборщик мусора для каждого файла.</p><h3>Три ошибки, которые ищутся дольше всего</h3><p><b>Рабочий поток не имеет доступа к window.</b> Причём не только напрямую: достаточно подключить библиотеку, которая трогает window на верхнем уровне модуля. В консоли разработчика появляется ошибка обращения к неопределённому объекту, а вот в интерфейсе не появляется ничего: если сбой рабочего потока никак не показан пользователю, тот видит вечный спиннер. Лечится ревизией всех импортов рабочего потока: библиотеки, завязанные на браузерное окружение, остаются в главном потоке и передают в рабочий поток уже готовые данные.</p><p><b>Округление сломало масштабирование.</b> Быстрый путь изменения размера использовал округлённый шаг по пикселям. На больших изображениях округление уводило исходную координату за пределы строки, и картинка выходила разрезанной по горизонтали с чёрной мозаикой внизу. Помогли точные дробные коэффициенты и ограничение по границам в каждом ручном проходе по пикселям.</p><p><b>Асинхронный обработчик сообщений перепутал порядок.</b> Обработчик стал ждать преобразование уже после кодирования, а асинхронные обработчики порядок не сохраняют: состояние поворота разъехалось с готовым изображением. Полностью синхронным такой обработчик не сделать, декодирование и кодирование асинхронны по своей природе. Работает другое: обрабатывать сообщения строго по одному, не начиная следующее до завершения предыдущего, и хранить состояние вроде угла поворота рядом с данными конкретного сообщения, а не в общей переменной.</p><p><b>Заголовки безопасности ломают рабочие потоки молча:</b><br />Блокирует загрузку скрипта заголовок Cross-Origin-Embedder-Policy: он требует, чтобы каждый сторонний ресурс отдавал разрешающий заголовок, иначе браузер молча его отбрасывает. Cross-Origin-Opener-Policy на это не влияет, но включают их обычно парой, поэтому и ломается всё в одном релизе. Любое изменение заголовков безопасности проверяйте на настоящем развёрнутом стенде, а не только на машине разработчика.</p><h2>Что это даёт в цифрах</h2><p>Скриншот 1920×1080 проходит такой путь: исходный PNG весит 2,1 МБ, после уменьшения и перевода в WebP с качеством 75% — 186 КБ, то есть минус 91%. Тот же кадр в AVIF занимает 102 КБ, минус 95% от исходного.</p><p>Именно эти проценты и отделяют страницу, которая грузится четыре секунды, от страницы, которая укладывается в одну.</p><h2>Что забрать с собой</h2><p>Порядок действий важнее набора инструментов. Сначала пиксели, потом формат под тип изображения, потом второй проход сжатия, и только затем разговор про кодеки нового поколения. Обратный порядок даёт красивую строчку в отчёте и почти никакого эффекта на реальной странице.</p><p>Материалы разбора: <a href="https://dev.to/_544bf5cbd223c35a49756/image-optimization-the-complete-2026-checklist-for-faster-websites-3pi5">чеклист оптимизации с замерами</a>, <a href="https://dev.to/uglypeardata/avif-format-guide-the-next-generation-image-compression-std-4oa5">техническое устройство AVIF и сравнительный тест форматов</a> и <a href="https://dev.to/_544bf5cbd223c35a49756/how-i-built-a-browser-image-compressor-that-handles-30-images-at-once-56k4">разбор браузерного конвейера сжатия</a>.</p><p>Откройте свою главную страницу с открытой панелью сети и отсортируйте запросы по размеру. Первые три строки почти наверняка окажутся картинками, и это ваша ближайшая оптимизация изображений: уменьшить их вдвое реально за один вечер.</p>]]></content:encoded>
    </item>
    <item>
      <title>SKILL.state от Google и Пердью: агент тратит в 50 раз меньше токенов</title>
      <link>https://tproger.ru/news/google-i-universitet-perdyu-pokazali-skill-state-v-skladskoj-si</link>
      <comments>https://tproger.ru/news/google-i-universitet-perdyu-pokazali-skill-state-v-skladskoj-si?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/google-i-universitet-perdyu-pokazali-skill-state-v-skladskoj-si</guid>
      <description><![CDATA[<p>Исследователи Google и Purdue University предложили SKILL.state: агент хранит вместо полной истории компактное JSON-состояние и патчит его на каждом шаге. На 200 шагах 122 тысячи токенов вместо 6,2 млн, точность 94% вместо 84%. Механизм, все цифры, ограничения и что можно повторить у себя.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/google-i-universitet-perdyu-pokazali-skill-state-v-skladskoj-si">SKILL.state от Google и Пердью: агент тратит в 50 раз меньше токенов</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 01 Sep 2026 17:28:20 GMT</pubDate>
      <content:encoded><![CDATA[<p>Исследователи из Google и Университета Пердью опубликовали на arXiv работу <a href="https://arxiv.org/abs/2608.26263">«SKILL.state: Scalable Long-Horizon Agent Skills»</a>, в которой предлагают ИИ-агенту не хранить в контексте всю историю действий. Вместо неё модель на каждом шаге получает три вещи: неизменную инструкцию навыка, компактное JSON-состояние задачи и последнее наблюдение, а отвечает патчем к состоянию и следующим действием. В складской симуляции на 200 шагов это дало в среднем по пяти запускам 122 тысячи токенов вместо 6,2 млн у агента с памятью-сводкой при росте точности с 84% до 94%. Первая версия статьи вышла 26 августа, вторая 28 августа; 1 сентября её пересказал vc.ru.</p><p>Если вы пишете агентов с накапливаемой историей (append-only), вы знаете эту боль: чем дольше сессия, тем дороже каждый следующий шаг, потому что модель заново перечитывает всё, что было. Работа интересна тем, что предлагает не сжимать историю, а заменить её структурой, и показывает на цифрах, что структура работает лучше сжатия. Оговорка сразу: результат сильно зависит от модели, а публичного кода авторы не выложили.</p><ul><li>Работа arXiv:2608.26263, авторы Санкет Бадхе, Приянка Тивари и Чонхён Чон из Google LLC и Purdue University; по пометке авторов на arXiv, принята на EMNLP.</li><li>На каждом шаге модель видит только инструкцию навыка, текущее JSON-состояние и последнее наблюдение; патч состояния проверяется, рассуждение после шага отбрасывается.</li><li>SkillExecBench (среда Warehouse Management) на Gemini 3 Flash: при 100 шагах 65 тысяч токенов против 1,25 млн у ReAct при точности 94% против 84%; при 200 шагах 122 тысячи против 6,2 млн у агента с памятью-сводкой.</li><li>При равном бюджете около 1 800 токенов на запрос простое усечение даёт точность 18%, LLMLingua 22%, SKILL.state 94%.</li><li>Ограничения: схема состояния задаётся заранее; задачи, где нужна сама история (аудит, объяснение действий), методу не подходят; один агент; публичного репозитория нет; на Gemma-4-31B и Qwen-3-8B при 100 шагах точность 42% и 34% против 94% у Gemini 3 Flash.</li></ul><h2>Как это устроено</h2><p>Обычный агент в стиле ReAct на шаге t получает весь журнал: системный промпт, все прошлые команды, все ответы инструментов и свои рассуждения. Суммарно за T шагов это O(T²) токенов, потому что каждый шаг перечитывает всё предыдущее. В SKILL.state промпт шага состоит из трёх частей, размер которых не зависит от номера шага при условии, что состояние и наблюдение ограничены по объёму: спецификация навыка P (что агент умеет и как должен себя вести), структурированное состояние Σ (JSON с тем, что важно для задачи) и последнее наблюдение O. Модель отвечает рассуждением, JSON-патчем к состоянию и действием; патч детерминированно проверяется и применяется, рассуждение после этого выбрасывается, действие исполняется.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-09-02/c899b3a4-bb38-4eac-ace6-16f77efea3b5.webp" alt="Схема одного шага агента SKILL.state" /><figcaption>Один шаг агента в SKILL.state: модель получает инструкцию, состояние и наблюдение, отдаёт патч состояния и действие, рассуждение отбрасывается. Схема: Tproger по статье arXiv:2608.26263.</figcaption></figure><p>Патч применяется операцией слияния: новые ключи добавляются, существующие перезаписываются, значение null удаляет ключ. Итог — суммарные затраты O(T) по токенам, а размер одного промпта почти не растёт: в экспериментах он держался в диапазоне 1 736–1 905 токенов при горизонте от 10 до 200 шагов. Схема состояния пишется один раз на домен, а не на задачу: для всех 100 задач InterCode CTF агент использовал одну статическую схему из пяти полей — найденные флаги, проверенные гипотезы, активные файлы, рабочий каталог, сводка команд.</p><h2>Цифры из работы</h2><p>Основной эксперимент — SkillExecBench, среда Warehouse Management: симуляция склада с длинными последовательностями действий, модель Gemini 3 Flash, пять случайных сидов, температура 0. При 100 шагах ReAct потратил 1 245 413 токенов при точности 0,84, baseline с явным состоянием в стиле LangGraph — 1 062 387 токенов и 0,91, SKILL.state — 65 408 токенов и 0,94. При 200 шагах агент с памятью-сводкой (Memory) ушёл в 6 175 509 токенов при точности 0,84, а SKILL.state уложился в 122 384 токена при 0,94. Именно отсюда «в 50 раз меньше» из пересказов; в дополнительном эксперименте с репозиторием кода при 100 шагах разница составила 1 848 500 против 90 200 токенов.</p><p>Авторы связывают выигрыш в точности не с сокращением текста, а с тем, что состояние сохраняет отношения между сущностями: какой шаг уже выполнен, что заблокировано, что кому должно. Два результата важнее заголовочных. Первый: при одинаковом бюджете около 1 800 токенов на промпт обычное усечение истории дало точность 0,18, сжатие через LLMLingua — 0,22, а SKILL.state — 0,94. Второй: в тесте с 50 нерелевантными событиями на каждом шаге обычный агент упал до 0,53, а SKILL.state удержал 0,98, потому что шум не попадает в состояние. После внешнего изменения среды и корректирующего уведомления SKILL.state в трёх сценариях из четырёх восстанавливался за 0 шагов, остальные подходы ошибались ещё 5–8 шагов; со сценарием отменённого заказа не справился никто.</p><p>На более привычных бенчмарках выигрыш скромнее, но есть: на InterCode CTF 54,2% Pass@1 против 43,2% у ReAct и 41,8% у Stateful, на τ-Bench Retail 58,3% и Airline 32,4% при снижении токенов до 3,47 млн и 2,88 млн соответственно.</p><h2>Чего в работе нет</h2><p>Ограничения авторы называют сами. Метод предполагает, что всё важное для будущих шагов можно вовремя записать в заранее заданную схему состояния: если структура задачи заранее неизвестна, если значимость детали осознаётся позже или если нужна сама история (аудит, происхождение данных, объяснение действий), подход не подходит. Второе: реализация рассчитана на одного агента, для нескольких понадобится разрешение конфликтов при одновременных записях в состояние. Третье: архитектура не привязана к Gemini, но качество сильно зависит от модели: при 100 шагах Gemini 3 Flash дала точность 0,94, Gemma-4-31B-it — 0,42, Qwen-3-8B-it — 0,34. Четвёртое, практическое: публичного репозитория авторы на 1 сентября не указали, хотя в статье есть промпты, псевдокод рантайма и детали реализации.</p><h2>Что можно повторить у себя</h2><ul><li>Опишите для своей задачи схему состояния из полей, которые реально нужны для следующего шага, и просите модель возвращать патч к ней, а не свободный текст; авторам для CTF хватило пяти полей.</li><li>Отбрасывайте рассуждения модели после шага: они нужны для выбора действия, а не для памяти.</li><li>Передавайте только последнее наблюдение целиком; всё, что из прошлых наблюдений имеет значение, должно уже лежать в состоянии.</li><li>Заведите тест на шум: подмешивайте в наблюдения нерелевантные события и смотрите, не тащит ли агент их в состояние.</li><li>Не ждите тех же 50 раз: в статье это симуляция склада на Gemini 3 Flash; на τ-Bench экономия заметно меньше, и ваш случай ближе ко второму.</li></ul><h2>Контекст</h2><p>Идея держать в контексте состояние вместо истории не нова: похожие приёмы есть в LangGraph и в собственных рантаймах команд, которые строят агентов в проде. Вклад работы в том, что она измеряет разницу на длинных горизонтах и сравнивает с методами сжатия при равном бюджете. Обсуждения на Hacker News и в блоге Google Research на момент публикации нет; следующий сигнал — появится ли код или воспроизведение на других моделях.</p><p>Источники: <a href="https://arxiv.org/abs/2608.26263">SKILL.state: Scalable Long-Horizon Agent Skills (arXiv:2608.26263)</a>, <a href="https://vc.ru/ai/3115725-google-i-universitet-perdyu-ii-agenta-s-ekonomiej-tokenov">Пересказ vc.ru</a></p><p>Изображение на обложке: Google</p>]]></content:encoded>
    </item>
    <item>
      <title>React-паттерн, который все используют, но он убивает производительность</title>
      <link>https://tproger.ru/articles/react-pattern-kotoryj-vse-ispolzuyut-no-on-ubivaet-proizvodite</link>
      <comments>https://tproger.ru/articles/react-pattern-kotoryj-vse-ispolzuyut-no-on-ubivaet-proizvodite?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/react-pattern-kotoryj-vse-ispolzuyut-no-on-ubivaet-proizvodite</guid>
      <description><![CDATA[<p>Почему React.memo перестаёт работать из-за inline-пропсов и как это исправить. Разбираем на примере со 200 строками и замерами.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/react-pattern-kotoryj-vse-ispolzuyut-no-on-ubivaet-proizvodite">React-паттерн, который все используют, но он убивает производительность</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 24 Jul 2026 04:56:37 GMT</pubDate>
      <content:encoded><![CDATA[<p>Вы обернули список в React.memo, добавили useCallback на обработчики и ждёте, что интерфейс полетит. Но при каждом вводе в поисковую строку список всё равно тормозит, а Profiler показывает 200 лишних рендеров. Чаще всего виноват не React, а один крошечный JSX-паттерн, который встречается в каждом втором компоненте.</p><p>В статье разбираем, почему React.memo сравнивает пропсы по ссылке, как inline-объекты и inline-функции обнуляют эту оптимизацию, и какие два простых приёма действительно возвращают производительность.</p><p>React.memo пропускает рендер только тогда, когда все пропсы равны по Object.is. Для объектов и функций это проверка по ссылке.</p><p>Inline-объекты и inline-колбэки в JSX создают новую ссылку на каждый рендер родителя, поэтому memo видит «новые» пропсы и снова рисует дочерний компонент.</p><p>В эксперименте со 200 memoизированными строками это дало 243,9 мс на один keystroke; после стабилизации ссылок — 6 мс.</p><p>Сначала выносите статические объекты за пределы компонента, а useCallback применяйте только там, где колбэк действительно пересекается с memoизированным потомком.</p><p>Оптимизировать всё подряд не нужно: измеряйте в Profiler, а не добавляйте хуки «на всякий случай».</p><h2>Как React решает, рендерить компонент заново или нет</h2><p>Когда родитель перерисовывается, React не делает послаблений дочерним элементам только потому, что они обёрнуты в React.memo. Он сравнивает новые пропсы со старыми. Если каждый проп проходит проверку Object.is, React может «отказаться» от рендера — bailout. Если хотя бы один проп не равен, компонент рисуется заново.</p><p>Для примитивов Object.is работает очевидно: 1 === 1 и 'hello' === 'hello'. А вот для объектов и функций сравнение идёт по ссылке.</p><p>Два объекта с одинаковым содержимым — это разные объекты в памяти. То же самое с функциями. Поэтому, когда вы пишете style=\{\{ padding: 16 \}\}, React получает новую ссылку на каждом рендере и считает проп изменившимся.</p><h2>Почему inline-пропсы — это не микрооптимизация, а поломка контракта</h2><p>Сам по себе объект в JSX не вреден. Если компонент дешёвый, редко перерисовывается и не обёрнут в memo, inline-пропсы почти не влияют на скорость. Проблема появляется, когда три условия накладываются друг на друга:</p><ul><li>родитель перерисовывается часто — поиск, скролл, фильтры, live-данные;</li><li>дочерний компонент или поддерево достаточно тяжёлые, чтобы лишний рендер был заметен;</li><li>вы уже добавили React.memo и ожидаете, что React будет пропускать работу.</li></ul><p>В такой ситуации нестабильные ссылки не просто добавляют накладных расходов — они полностью отменяют ту оптимизацию, ради которой вы взяли React.memo. UI продолжает работать, но лагает ввод, тормозят списки, а в Profiler видно, что дерево горит жёлтым при каждом чихе.</p><h3>Классический опасный пример</h3><p>Представьте список товаров из 200 строк. Каждая строка обёрнута в memo, но в месте вызова передаются inline-пропсы:</p><p>Здесь style и onAddToCart создаются заново при каждом рендере ProductList. Для memo это сигнал, что у каждой строки изменились пропсы, и все 200 компонентов рисуются снова.</p><h2>Эксперимент: от 243,9 мс до 6 мс на один keystroke</h2><p>Автор оригинальной статьи собрал контрольный пример: поисковый интерфейс со 200 memoизированными строками. Все строки получают одинаковые логические значения, но новые ссылки на объекты и функции. Результат после шести введённых символов: каждая видимая строка отрендерилась 14 раз.</p><p>В React DevTools Profiler один keystroke дал коммит длительностью 243,9 мс, в котором подсветились все 200 волокон строк. Инструмент @welldone-software/why-did-you-render прямо указал причину: props.style — «different objects that are equal by value», props.onAddToCart — «different functions with the same name».</p><p><b>Почему 14 рендеров?</b><br />
Каждый ввод в поиск меняет searchTerm, родитель перерисовывается, и inline-пропсы дают строкам новые ссылки. Счётчик рендеров растёт на каждый keystroke, даже если отфильтрованные товары не изменились.</p><h2>Как починить: два приёма вместо дюжины хуков</h2><p>Чтобы восстановить bailout, нужно сделать так, чтобы неизменяющиеся значения не получали новую ссылку на каждом рендере. Правило простое: сначала вынести, потом закешировать.</p><h3>1. Статические объекты — за пределы компонента</h3><p>Если объект не зависит от пропсов и состояния, создайте его один раз на уровне модуля. Это дешевле любого хука и не требует dependency-массива.</p><h3>2. Динамические колбэки — useCallback</h3><p>Если функция передаётся в memoизированный компонент и не должна меняться без причины, оберните её в useCallback со стабильным массивом зависимостей.</p><p>После этих двух изменений в эксперименте время рендера упало с 243,9 мс до 6 мс, счётчики строк застыли на 2, а Why Did You Render замолчал — avoidable re-renders исчезли.</p><h2>Когда не нужно ничего стабилизировать</h2><p>Главная ошибка — оборачивать в useCallback каждую функцию и выносить каждый объект за компонент. React сам по себе быстрый, а мемоизация — это контракт, а не стиль кодирования.</p><ul><li>Компонент дёшев и редко перерисовывается — не тратьте когнитивный бюджет команды.</li><li>Дочерний элемент не обёрнут в React.memo — тогда стабильные ссылки ничего не экономят.</li><li>Значение зависит от часто меняющегося состояния — useCallback с нестабильным массивом зависимостей всё равно будет пересоздаваться.</li><li>Вы ещё не замерили в Profiler — оптимизация без измерений почти всегда лишняя работа.</li></ul><p>React Compiler, который сейчас выходит в стабильное состояние, автоматически мемоизирует многое из того, что раньше делали вручную. Но и он не отменяет понимания ссылочной стабильности: useMemo и useCallback остаются полезными, когда нужен точный контроль, например для зависимостей эффектов.</p><h2>Часто задаваемые вопросы</h2><h2>Выводы</h2><p>Inline-объекты и inline-колбэки в JSX — не антипаттерн сами по себе. Они становятся проблемой только на границе memoизации, где React ожидает стабильные ссылки. Как только вы понимаете это правило, многие «таинственные» лишние рендеры перестают быть таинственными.</p><ol><li>Профилируйте до оптимизации, а не после.</li><li>Вынесите статические объекты за компонент — это самый дешёвый способ стабилизации.</li><li>Применяйте useCallback только для колбэков, которые уходят в memoизированные потомки.</li><li>Проверяйте себя через React DevTools Profiler и Why Did You Render.</li><li>Не забывайте про React Compiler, но не полагайтесь на него как на волшебную палочку.</li></ol><blockquote>Мемоизация — это контракт. Если потомок рассчитывает на стабильные ссылки, родитель должен их обеспечить. Нарушение этого контракта стоит намного дороже, чем отсутствие мемоизации вовсе.</blockquote><p><b>Источник:</b> <a href="https://blog.logrocket.com/react-pattern-everyone-uses-kills-performance/" rel="noopener noreferrer">LogRocket — The React pattern everyone uses that kills performance</a>.</p><p>Проверьте свой текущий проект: откройте React DevTools Profiler, введите что-нибудь в поиск и посмотрите, сколько компонентов подсветится жёлтым только из-за новой ссылки в пропсах.</p>]]></content:encoded>
    </item>
    <item>
      <title>Можно ли аналитику в 2026 году положиться на ИИ и агентов или ещё нет?</title>
      <link>https://tproger.ru/articles/mozhno-li-analitiku-v-2026-godu-polozhitsya-na-ii-i-agentov-ili-eshh</link>
      <comments>https://tproger.ru/articles/mozhno-li-analitiku-v-2026-godu-polozhitsya-na-ii-i-agentov-ili-eshh?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/mozhno-li-analitiku-v-2026-godu-polozhitsya-na-ii-i-agentov-ili-eshh</guid>
      <description><![CDATA[<p>Ведущий системный аналитик тестирует ИИ в реальном процессе: Use Case, Confluence, Figma, API-спецификации. Где агент помогает, где придумывает лишнее и почему без процесса он опасен.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/mozhno-li-analitiku-v-2026-godu-polozhitsya-na-ii-i-agentov-ili-eshh">Можно ли аналитику в 2026 году положиться на ИИ и агентов или ещё нет?</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Аналитика]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 15 Jul 2026 11:23:35 GMT</pubDate>
      <content:encoded><![CDATA[<p>В какой-то момент у нас, как и у многих команд, появился соблазн проверить: а можно ли уже не просто просить AI «напиши user story», а действительно встроить его в рабочий процесс аналитика? Например, дать агенту вводные по задаче, макеты в Figma, примеры документации и  требования к оформлению, и получить на выходе нормальный Use Case, API-спецификацию, PlantUML-диаграмму и аккуратную страницу в Confluence.</p><p>Звучит красиво.</p><p>Особенно если вы когда-нибудь вручную переносили сценарии из заметок в Confluence, сверяли шаги с макетами, оформляли вкладки с HTTP-запросами, проверяли коды ошибок и пытались не забыть все вопросы, которые «надо потом уточнить».</p><h2>Пролог</h2><p>Всем привет! Я Золотарёва Виктория, ведущий системный аналитик в команде Financial Transaction Service или просто FinService. В марте 2026 года активно обсуждалось внедрение ИИ в рабочие процессы. Аналитиков это тоже коснулось.</p><p><b>Моя первая реакция была скептической: зачем мне агент, если свою работу я могу сделать быстрее сама?</b></p><p>Первые эксперименты этот скепсис только подтвердили. К примеру, я могла за рабочий день самостоятельно подготовить User Story и полноценный Use Case с контрактами, эндпоинтами, диаграммой и макетами. С агентом та же задача растягивалась на 3-4 дня: нужно было передать контекст, объяснить формат результата, постоянно направлять агента, а затем ещё и внимательно проверять, что он успел придумать по дороге.</p><p>Однажды в начале исследования агент предложил неверный контракт. Я явно указала на ошибку и передала актуальное решение. Но в финальной документации снова увидела неправильный контракт с неймингом, похожим на первоначальный. Агент вернулся к собственному старому решению и представил его как <b>подтверждённое мной</b> (а вот это уже опасно).</p><p>Проблема AI в аналитике не всегда в том, что он пишет очевидную чушь. Иногда он очень убедительно пишет неправильные вещи.</p><p>Также основную аналитическую документацию мы описывали в Confluence, а разработчики работали с кодом и агентами в GitLab. Чтобы агент разработчика мог начать работу, документацию из Confluence приходилось передавать ему как контекст. Затем он заново анализировал требования, формировал Markdown-спецификацию в GitLab и только после этого переходил к реализации.</p><p>Получалось, что один и тот же контекст фактически описывается дважды.</p><p>В какой-то момент мы с разработчиком задались вопросом: а зачем дважды описывать один и тот же сценарий сначала в Confluence, а потом GitLab?</p><p><b>«Что, если аналитик сразу подготовит спецификацию, которую агент разработчика сможет использовать почти без дополнительной переработки?»</b></p><p>С этого и началось данное исследование — я решила проверить, насколько мы близки к этой утопии, и протестировала, как ИИ работает в реальном аналитическом процессе в нескольких кейсах:</p><ul><li>research-фазы для подготовки Use Case;</li><li>подготовка аналитических артефактов: user story, use case, API-спецификаций, PlantUML-диаграмм и Confluence-структуры;</li><li>публикация в Confluence (здесь мы использовали агентов и, что примечательно, выявили важные технические ограничения, связанные с авторизацией, storage-разметкой, вложениями, кодировкой и передачей больших данных (но об этом в статье);</li><li>работа с Figma: настройка токена, обращение к API, чтение нод и экспорт экранов в PNG.</li></ul><p>В статье расскажу, где AI действительно помог, где уверенно начал придумывать лишнее, почему Confluence оказался сложнее, чем ожидалось, и какие правила я бы теперь закладывала в любой агентный сценарий для аналитика.</p><p>Спойлер: до магической кнопки далеко. Но мы получили отличный (и довольно болезненный) опыт того, как ИИ может помочь в аналитике, а где он пока просто спотыкается и обламывается.</p><p><b>Для самых внимательных в конце оставлю ссылку на диск со всеми style guide и результатом генерации, чтобы вы могли самостоятельно проверить подход в своей работе —&gt;</b></p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-07-15/d64b58d9-ec33-4a68-9b75-b74b3e704d4f.webp" alt="" /><figcaption>Рабочее пространство/область работы с агентом Cursor</figcaption></figure><h2>Четыре гипотезы</h2><p>Исследование было направлено на проверку нескольких практических гипотез.</p><ul><li>Первая гипотеза заключалась в том, что AI может ускорить подготовку аналитических артефактов: user story, use case, acceptance criteria, API-спецификаций и описаний бизнес-процессов.</li><li>Вторая была связана с качеством требований: проверялось, может ли AI не только писать текст, но и выявлять пропуски, противоречия, неоднозначные формулировки и открытые вопросы.</li><li>Третья гипотеза касалась агентных сценариев — можно ли использовать AI-агента как участника рабочего процесса: чтобы он читал макеты, работал с файлами, формировал спецификации, готовил Confluence-разметку и помогал публиковать результат.</li><li>Четвертая гипотеза касалась ограничений. Было важно не только зафиксировать успешные кейсы, но и понять, где агентный подход становится нестабильным: в авторизации, публикации, работе с вложениями, передаче изображений, кодировке или сетевых ограничениях.</li></ul><p>В результате исследование охватило не только «полезные сценарии», но и реальные технические сбои, которые важно учитывать при внедрении AI в аналитическую работу.</p><h2>Использовать ИИ, чтобы написать требования — сразу нет</h2><p>В списке выше нет гипотез «Может ли ИИ написать требования с нуля», потому что первый же эксперимент показал, что для аналитической работы недостаточно просто попросить AI «написать требования». Такая формулировка может дать быстрый черновик, но не гарантирует корректность.</p><p>К примеру:</p><ul><li>если во входных данных отсутствует endpoint, AI может предложить его по аналогии;</li><li>если не описаны ошибки, он может добавить типовые коды;</li><li>если в макете не виден текст кнопки, AI может восстановить его по смыслу.</li></ul><p>Внешне результат выглядит аккуратно, но для разработки и тестирования такие допущения опасны, потому что ИИ подменяет неизвестность своими выдумками.</p><p><b>Поэтому в исследовании был использован более строгий принцип: AI не должен закрывать пробелы самостоятельно. Если данных недостаточно, он должен фиксировать неопределенность и задавать уточняющие вопросы.</b></p><p>Для этого был применен целевой уровень неопределенности — 0,1. Это означает, что перед подготовкой финального артефакта все ключевые вопросы должны быть закрыты или явно вынесены в список открытых вопросов. Если неопределенность выше, агент не должен формировать финальный use case, domain-spec или диаграмму.</p><p>Такой подход позволил изменить роль AI. Он перестал быть просто генератором текста и стал инструментом предварительного анализа: помогает собрать вводные, найти недостающие данные и подготовить основу для дальнейшей спецификации.</p><h2>Spec Driven Development</h2><p>Следующая идея появилась после разговора с разработчиком, — чтобы ИИ не ушел в свободный полет, мы приручили его методологией Spec Driven Development. В контексте системного анализа он был применен не как подход к автоматической генерации кода, а как способ выстроить управляемую цепочку аналитических артефактов.</p><p>Суть подхода в том, что спецификация становится центральным рабочим артефактом. Работа начинается не с написания итогового документа, а с последовательного уточнения того, что должно быть реализовано, какие сценарии должны поддерживаться, какие данные нужны, какие ограничения существуют и какие вопросы остаются открытыми.</p><p>В исследовании этот подход был адаптирован следующим образом:</p><p>Исследование → Открытые вопросы → Зафиксированные факты → Domain-spec → OpenAPI → Confluence UC</p><p>На первом этапе собираются вводные и фиксируются неопределенности. На втором этапе подтвержденные факты переносятся в findings. После этого формируется domain-spec, который становится основой для API-спецификации, диаграммы взаимодействия и страницы use case в Confluence.</p><p>Такой процесс снижает риск того, что AI перенесет ошибку из раннего этапа в финальный документ.</p><ul><li>Если вопрос не закрыт, он остается в research-фазе.</li><li>Если поле API не подтверждено, оно не должно появляться в спецификации.</li><li>Если сценарий не согласован, он не должен попадать в Confluence как финальное требование.</li></ul><p>Spec Driven Development в этом контексте оказался полезен не как модная методология, а как практический способ дисциплинировать работу с AI.</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-07-15/eb0ea28e-02a2-4598-9fa7-72878daeedcb.webp" alt="" /><figcaption>Дерево файлов проекта в IntelliJ IDEA: здесь хранятся промпты, правила, style guide, материалы по UC и инструменты для публикации документации в Confluence</figcaption></figure><h2>Итак, исследование</h2><p>Дальше вас ждут несколько кейсов. Один кейс может проверять несколько гипотез.</p><h3>Кейс №1. Подготовить Use Case? Да, но с нюансами</h3><p>Задача research-фазы — собрать все данные, необходимые для будущего UC: цель сценария, точки входа, пользовательские действия, данные для отображения, API-вызовы, успешные и ошибочные ответы, предусловия, постусловия, бизнес-правила, ограничения и исключения из scope. Решили проверить, как с этим справится агент и «скинули» ему задачи анализировать вводные, фиксировать подтвержденные факты и выявлять недостающую информацию, при этом агент не должен писать финальный UC.</p><p>Для этого были определены рабочие файлы:</p><ul><li>README.md — общий контекст и статус (в т.ч. та самая неопределенность).</li><li>open-questions.md — журнал вопросов, зависимостей и допущений.</li><li>findings.md — только подтвержденные факты.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-07-15/e4542d19-dc0c-43b1-920b-f14cd7a22c6c.webp" alt="" /><figcaption>findings.md, куда агент в ходе исследования складывает подтверждённые факты, сравнение «до/после» и найденные изменения, чтобы затем на их основе подготовить domain-spec при неопределённости не выше 0,1</figcaption></figure><p>Такой формат оказался удобен для работы с неполными вводными. Он помогает не терять вопросы, отделять факты от предположений и не переносить спорные решения в финальную документацию.</p><p><b>Основной вывод из этого кейса:</b> до применения такого подхода AI мог сразу предлагать готовый use case, даже если часть информации отсутствовала. После введения research-фазы процесс стал более контролируемым: сначала фиксируются пробелы, затем уточняются данные и только после этого формируется спецификация.</p><h3>Кейс №2: Подготовка аналитических артефактов</h3><p>Следующим направлением стала подготовка style guide и промтов для разных типов аналитических документов. Style guide был сделан с помощью модели LLM на основе ранее составленных user story.</p><p>Сначала я пыталась описать все правила работы агента одной большой инструкцией, но затем агент начал смешивать требования разных артефактов: правила User Story переносились в Use Case, подход к UC влиял на API-спецификацию. <b>Поэтому каждый тип артефакта получил собственный style guide.</b></p><p>Первые правила я описывала с нуля на основе своего опыта и уже выстроенного формата документации.</p><ul><li>Были отдельно оформлены правила для user story, research-фазы, REST API-спецификации и Confluence use case. Это оказалось важным, потому что AI существенно лучше работает, когда ему задан не только контекст задачи, но и точный формат результата.</li><li>Для user story были определены правила формулировки пользовательской цели, результата и критериев приемки. Отдельно было зафиксировано, что одна story должна описывать одну пользовательскую цель, а критерии приемки должны быть проверяемыми.</li><li>Для API-спецификации были определены правила описания сценария, endpoint, параметров, тела запроса, успешных ответов, типовых ошибок и PlantUML-диаграммы. Отдельно было зафиксировано, что агент не должен придумывать поля или коды ошибок, если они отсутствуют в источнике.</li><li>Для Confluence use case были заданы более строгие правила, так как это уже не просто текстовый документ, а страница с конкретной структурой и макросами. В нее должны входить разделы основной информации, истории изменений, предусловий, сценария использования и схемы работы. HTTP-детали должны оформляться во вкладках, запросы и ответы — в code-блоках, бизнес-правила — в info-блоках, а диаграмма — в PlantUML.</li></ul><p><b>Основной вывод из этого кейса:</b> AI может ускорить подготовку артефактов, если для него заранее определены правила качества. Без style guide результат будет зависеть от интерпретации модели. Со style guide результат становится более повторяемым и пригодным для командной работы.</p><h3>Кейс №3: Публикация UC в Confluence (или почему здесь начался ад)</h3><p>Наиболее сложным и показательным стал кейс подготовки и публикации use case в Confluence.</p><p>На первый взгляд задача простая: сформировать страницу в формате команды и опубликовать её.  Страница должна была содержать оглавление, историю изменений, вкладки, code- и info-блоки, PlantUML. Обычного Markdown здесь недостаточно. Нельзя было и просто взять HTML-выгрузку существующей страницы и отправить её обратно как body.storage. Если так делать, то мы получали некорректную страницу, битые изображения и ссылки на attachments исходного документа (а ещё море слёз).</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-07-15/8fda2234-048c-4c1d-820b-690b4d339dac.webp" alt="" /></figure><p>Как оказалось, генерация текста и публикация в корпоративную вики — это два совершенно разных уровня сложности. И вот почему:</p><p><b>1. Проблема авторизации.</b> В Figma был PAT, и агент работал сам по себе. В нашем корпоративном Confluence отдельного технического токена не было. В итоге агент работал от лица пользователя, используя куки активной вкладки браузера (credentials: 'same-origin'). То есть, если вы не залогинены в Confluence, агент видит страницу входа.</p><p><b>2. Проблема формата (Storage XML).</b> Нельзя просто взять HTML-выгрузку страницы и отправить её как body storage. Confluence это съест, но картинка сломается, а ссылки превратятся в мусор. Нам пришлось писать Python-скрипт, который генерировал чистую Confluence storage-разметку.</p><p><b>3. Проблема версий.</b> Confluence требует актуальный номер версии (version.number + 1 при PUT-запросе). Если кто-то зашёл и поправил страницу пока ИИ думал — конфликт обеспечен. Приходилось перед публикацией запрашивать status=current с отключением кэша.</p><p><b>4. Боль с кодировкой.</b> Классическая засада. Передавали storage через base64, использовали стандартный atob(b64) для декодирования — и вся кириллица превратилась в кракозябры. Пришлось переписывать на new TextDecoder('utf-8').decode(raw).</p><p><b>5. Ограничения CDP.</b> Confluence storage весит десятки килобайт, а если добавить картинки — мегабайты. Прогонять такие объёмы данных через последовательные вызовы Runtime.evaluate (Chrome DevTools Protocol) оказалось нестабильным — связь рвалась.</p><p>В итоге мы пришли к такому пайплайну: Python-генератор → Storage XML → Локальная валидация → Локальный CORS-сервер → Browser REST (с куки) → TextDecoder → Выполнение сниппета в DevTools.</p><p>Как раз тогда и появилась идея фиксировать все проблемы и итоговые рабочие решения в отдельный документ в <a href="http://confluence-generation-retrospective.md">confluence-generation-retrospective.md</a>. Это позволило агенту не зацикливаться на одних и тех же предложенных вариантах решения, а полноценно и автономно решать вопросы по уже зафиксированным рабочим решениям.</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-07-15/22027adc-690b-48b9-aa52-654f566cba7a.webp" alt="" /><figcaption>Пример собранной документации Cursor в Confluence</figcaption></figure><h3>Кейс №4: Работа с Figma</h3><p>Отдельным направлением исследования стала работа с Figma, так как макеты пользовательских сценариев хранятся именно там.</p><p>Для доступа к Figma был настроен Personal Access Token. Токен был сохранен в переменную окружения FIGMA_TOKEN, после чего агент мог обращаться к Figma API через заголовок X-Figma-Token.</p><p>Проверка подключения выполнялась через endpoint /v1/me. После подтверждения токена агент мог получать данные о файле, читать структуру нод и экспортировать отдельные экраны в PNG.</p><p>Пример проверки:</p><p>curl -s -H “X-Figma-Token: $FIGMA_TOKEN” <a href="https://api.figma.com/v1/me">https://api.figma.com/v1/me</a> | python3 -m json.tool</p><p>Для работы с конкретными экранами использовались file key и node id. При этом была зафиксирована важная техническая особенность: node id из ссылки Figma может отображаться через дефис, но в API передаётся через двоеточие. Например, 4888-28266 в URL передается как 4888:28266 в API-запросе.</p><p>В результате агент смог получать макеты, экспортировать экраны в PNG, читать изображения с диска и использовать их для описания сценариев.</p><p>Отдельно было выявлено, что работа токена в обычном терминале не гарантирует работу агента в Cursor. Для доступа агента к <a href="http://api.figma.com">api.figma.com</a> может потребоваться разрешение сетевого доступа в sandbox-настройках.</p><h2>Промежуточный вывод</h2><p>После первых кейсов стало понятно: я постепенно перешла от разовых экспериментов с AI к оформленному рабочему подходу.</p><p>Агент уже не был отдельным чатиком, которому я каждый раз заново объясняю задачу. У него появились правила, область ответственности, рабочие файлы, критерии остановки research-фазы и понятный формат результата. <b>Но вместе с этим появился новый риск.</b></p><p>Все эти эксперименты я проводила в своей предметной области и на артефактах, которых хорошо знала. Я понимала, каким должен быть результат, где агент ошибается, какие поля нельзя придумывать, какие формулировки не подходят и какие технические детали нужно перепроверить.</p><p>То есть на этом этапе мы доказали не то, что подход универсален, а только то, что он помогает мне быстрее выполнять знакомую работу при моём же постоянном контроле.</p><p>А этого было недостаточно.</p><p><b>Чтобы эксперимент не превратился в историю «я хорошо написала промты под свои задачи», нужно было проверить подход на другом артефакте, в другой предметной области и в формате документации, который изначально не был заточен под мой привычный рабочий процесс.</b></p><h2>Бонусная глава, где я экспериментирую на чужой документации</h2><p>Все эксперименты я проводила на собственной работе, в знакомой предметной области и на документации, формат которой хорошо знала. Задачи, которые раньше оценивались примерно в пять story points, теперь укладывались в один-два с учётом проверки. И к этому моменту оставался один вопрос: <b>«Действительно ли у меня получилось построить воспроизводимый процесс или я просто научила агента работать «как я»?»</b></p><p>Чтобы подтвердить или опровергнуть свою догадку, я предложила коллеге из другого отдела провести эксперимент на другой документации. Мы взяли реальную задачу по актуализации middle-документации сервиса. Она звучала так: «Разработчик уже добавил новую функциональность, OpenAPI и тесты. Базовый каркас документации существовал, но полноценного раздела по новой функциональности ещё не было».</p><p>При ручной работе сначала потребовалось бы изучить существующую документацию, затем собрать детали реализации у разработчика, написать и полдня отшлифовать описание и отдельно дождаться сниппетов.</p><p>Мы начали не с команды «Внеси в документацию все изменения разработчика», а согласно уже проложенному сценарию (который описала выше).</p><p>На первой фазе агент должен был исследовать изменения feature-ветки относительно основной и определить:</p><ul><li>Что изменилось?</li><li>Что действительно реализовано?</li><li>Что из этого нужно переносить в документацию?</li></ul><p>В качестве источника истины он использовал код, OpenAPI и уже существующую документацию из Confluence для получения полного контекста. Согласно правилам все подтверждённые факты фиксировались в findings.md, а неясности в <a href="http://open-questions.md">open-questions.md</a>.</p><p>Research-фаза выявила методы, которые уже появились в коде, но отсутствовали в документации, а также расхождения между контрактом и фактической реализацией.</p><p>Эти расхождения не скрывались, а фиксировались через NOTE.</p><p>Параллельно мы сравнивали результат с другой документационной веткой, но не использовали её как безусловный источник истины.</p><p>Также мы четко разграничили правилами:</p><p>- Если в документации был метод, которого нет в нашем API, мы его не переносили.</p><p>- Если описание обещало больше, чем делал код, фиксировали расхождение.</p><p>Получился двухфазный процесс: <b>исследование изменений с минимальной неопределенностью → перенос подтвержденных фактов в техническую документацию.</b></p><p>Полное описание со сниппетами заняло примерно полдня. Часть времени при этом ушла на первую настройку среды и плагина (не все плагины так легко настраиваются или просто мне с idea не везет).</p><p><b>Но главное — эксперимент шёл параллельно с ручной работой другого аналитика над той же задачей.</b></p><p>После завершения мы сравнили результаты. Существенных расхождений в сценариях и технических фактах не оказалось, различались в основном формулировки.</p><p>Например, ручное описание начиналось примерно так:</p><p>&gt; В сервисе реализованы эндпоинты для управления пакетами поставок. Пакеты используются для группировки изменений сущностей с целью последующего выполнения действий: отправки на ревью, развертывания и других операций.</p><p>Агент сформулировал вводный текст немного иначе:</p><p>&gt; В сервисе реализованы эндпоинты для админки по работе с поставками. Поставки используются для группировки изменений сущностей перед согласованием и последующей выкаткой. Доступ к данным эндпоинтам извне ограничивается шлюзом доступа.</p><p><b>Смысл в обоих вариантах совпадал: речь шла о новой функциональности для группировки изменений и управления их жизненным циклом. Но агент, следуя style guide, сделал вводный текст более структурным: сначала указал, для кого предназначено API, затем объяснил назначение поставок, а отдельно вынес ограничение доступа.</b></p><p>При этом детали реализации не попадали в intro. Они оставались в описании конкретных методов, чтобы вводный раздел не превращался в техническую свалку.</p><p>Для меня это стало одним из главных результатов исследования.</p><p>Агент справился с другой предметной областью и другим форматом документации не потому, что «умеет писать middle-документацию», а потому что сработал процесс.</p><p>Но есть неприятный нюанс, который нужно учитывать при работе с агентом: результат нужно уметь проверить.</p><p>Здесь легко сделать неправильный вывод.</p><p>Раз агент смог работать с технической документацией, значит аналитик может заходить на территорию разработки и самому покрывать код тестами вместо разработчика</p><p>Нет.</p><p>Результат получился качественным потому, что его можно было проверить.</p><p>В аналитической части я понимала структуру сценариев, правила описания контрактов и могла заметить, когда агент начинает додумывать.</p><p>В эксперименте с middle-документацией источником истины были код, OpenAPI и тесты. Результат дополнительно сравнивался с работой другого аналитика.</p><p>Но без достаточной экспертизы в разработке я не могу утверждать, что сгенерированный AI сниппет жизнеспособен. Я могу проверить, соответствует ли он описываемому сценарию и выполняет ли функцию примера в документации.</p><p>И вот здесь и появляется риск <b>локальной оптимизации.</b></p><p>Аналитик может сэкономить два часа на генерации сниппетов, а разработчик затем потратить четыре на review, конфликты и исправление неработающего кода.</p><p>Это не оптимизация. Это перенос работы на следующего участника процесса.</p><p>Поэтому нужно добавить ещё одно правило: <b>не оценивайте качество AI-результата там, где у вас нет экспертизы его проверить.</b></p><p>Хороший промт не превращает аналитика в разработчика. AI ускоряет существующую экспертизу, но не создаёт её автоматически.</p><p>Зоны ответственности остаются прежними: аналитик отвечает за требования и спецификацию, разработчик — за реализацию, тестировщик — за качество проверок. AI лишь помогает каждой роли работать быстрее со своим контекстом.</p><h2>Что изменилось в процессе команды</h2><p>В конце квартала мы вынесли результаты на общую встречу и сравнили артефакты, подготовленные вручную и с участием агента.</p><p>Стало понятно, что основной эффект — не только в экономии времени аналитика.</p><p>Раньше один и тот же контекст фактически описывался на двух платформах:</p><p>Аналитик готовил основной документ в Confluence, а затем требования повторно переносились и интерпретировались в GitLab для разработки и агентов.</p><p>После эксперимента мы решили сосредоточиться на одном полноценном артефакте. и процессе:</p><p>- Аналитик формирует подтверждённый контекст.</p><p>- Агент разработчика использует спецификацию как вход для реализации.</p><p>- Тестировщик на основе спецификации подготавливает тест-кейсы.</p><p>Мы не убрали документацию из Confluence полностью, а убрали повторный перенос и интерпретацию одного и того же контекста.</p><h2>Заключение</h2><p>В марте я начинала с простого вопроса: «Зачем мне агент, если сама я сделаю быстрее?»</p><p>Первые эксперименты давали однозначный ответ: <b>не нужен.</b></p><p>Задача «на день» растягивалась на три-четыре. Агент требовал постоянного контроля, придумывал данные и возвращал собственные решения в финальный документ как мои, а после запрета на додумывание начинал бесконечно задавать вопросы.</p><p>Постепенно стало понятно: мы давали агенту задачу, но не давали процесс. В итоге полезным оказался не «идеальный промт», а система ограничений: research → open questions → findings → domain-spec → API → документация.</p><p>А также:</p><p>- Отдельные style guide.</p><p>- Явные источники истины.</p><p>- Контроль неопределённости.</p><p>- Чёткие границы ответственности.</p><p>И к концу исследования вопрос изменился на «Можно ли один раз подготовить качественную спецификацию и перестать пересобирать один и тот же контекст отдельно для аналитика, разработчика и тестировщика?»</p><p>В нашем случае — да.</p><p><b>При этом мой главный вывод за квартал остаётся довольно жёстким — ИИ эффективен в аналитике только тогда, когда заперт в управляемый процесс. Без процесса он ускоряет не только создание документации, но и распространение ошибок. А с процессом, style guide, контролем неопределённости и ручной проверкой он становится полезным ассистентом.</b></p><p>AI ускоряет, он не заменяет вашу экспертизу. Если вы не способны проверить результат, вы не контролируете агента. Вы просто надеетесь, что он угадал. А на требованиях, API, коде и архитектурных решениях я бы на удачу пока не рассчитывала.</p><p>____________</p><p>Чтобы статья не осталась только рассказом о моем опыте, я собрала отдельную папку с материалами эксперимента.</p><p>[<a href="https://disk.yandex.ru/d/psoAL5N4basfRg">Ссылка на материалы</a>]</p><p><b>Важно: материалы обезличены и очищены от чувствительных данных. Их не стоит воспринимать как универсальный шаблон «на все случаи жизни», но можно использовать как отправную точку для собственного процесса.</b></p>]]></content:encoded>
    </item>
    <item>
      <title>KolibriOS 0.7.7: ОС на ассемблере, которая влезает на дискету</title>
      <link>https://tproger.ru/articles/kolibrios-0-7-7-os-na-assemblere-kotoraya-vlezaet-na-disketu</link>
      <comments>https://tproger.ru/articles/kolibrios-0-7-7-os-na-assemblere-kotoraya-vlezaet-na-disketu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kolibrios-0-7-7-os-na-assemblere-kotoraya-vlezaet-na-disketu</guid>
      <description><![CDATA[<p>KolibriOS 0.7.7 — российская ОС на ассемблере, которая помещается на дискету и работает на i586 с 8 МБ ОЗУ. Разбираем возможности и ограничения.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kolibrios-0-7-7-os-na-assemblere-kotoraya-vlezaet-na-disketu">KolibriOS 0.7.7: ОС на ассемблере, которая влезает на дискету</a>»</p>]]></description>
      <category><![CDATA[Низкоуровневое программирование]]></category>
      <category><![CDATA[Ретро]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Операционные системы]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Hardware]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Браузеры]]></category>
      <category><![CDATA[Язык ассемблера]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 04 Jul 2026 12:28:13 GMT</pubDate>
      <content:encoded><![CDATA[<p>В 2026 году операционная система, которая полностью умещается на 3,5-дюймовой дискете и загружается быстрее, чем вы успеваете отпить кофе, звучит как технологическая шутка. Но <b>KolibriOS 0.7.7</b> существует, развивается и даже имеет рабочий графический стол. Это не эмулятор и не демо: полноценная 32-битная ОС, ядро которой написано на ассемблере.</p><p>В своей колонке <i>Daily Drivers</i> редактор Hackaday Дженни Лист скачала ISO-образ, запустила KolibriOS на современном ThinkPad и удивилась: система выглядит зрелой, стабильной и по-настоящему быстрой. Разбираем, что это за проект, откуда он взялся и кому пригодится сегодня.</p><h2>Что такое KolibriOS</h2><p><b>KolibriOS</b> — российский форк MenuetOS, созданный в 2004 году. В то время как автор оригинального MenuetOS Вилле Турьянмаа переключился на закрытую 64-битную версию, сообщество вокруг русскоязычного порта сохранило код открытым и продолжило развивать 32-битную ветку. Название «Колибри» выбрано неслучайно: система маленькая и быстрая, как птица-колибри.</p><p>Проект поддерживается преимущественно разработчиками из России, Казахстана, Украины и Германии. Код распространяется под открытой лицензией, а сборки регулярно публикуются на официальном сайте и зеркалах сообщества.</p><h2>Версия 0.7.7</h2><p>Версия 0.7.7 продолжает философию предельного минимализма. Минимальные системные требования смехотворны по нынешним меркам: <b>1 МБ</b> места на диске, <b>8 МБ</b> оперативной памяти и процессор класса <b>i586</b>. При этом система выдаёт графический интерфейс, многозадачность и набор встроенных программ.</p><p>Дженни Лист отмечает, что на ThinkPad 2020-х годов KolibriOS загружается «в мгновение ока» и сразу показывает рабочий стол. Интерфейс выглядит пиксельным, как в 1990-х: нет сглаживания, есть ощущение ретро. Но при этом всё работает — без синих экранов и зависаний.</p><ul><li>KolibriOS 0.7.7 — 32-битная ОС с ядром на ассемблере, базовый образ весит около 1 МБ.</li><li>Минимальные требования: i586, 8 МБ ОЗУ, 1 МБ на диске.</li><li>В комплекте браузеры, игры, эмуляторы, графические редакторы и среда разработки.</li><li>Главное ограничение для повседневного использования — отсутствие поддержки HTTPS.</li><li>Лучше всего подходит для старого железа, тонких клиентов, обучения и ретрокомпьютинга.</li></ul><h2>Как устроена система</h2><h3>Ядро на ассемблере</h3><p>Большая часть KolibriOS написана на <b>FASM</b> — плоском ассемблере. Это объясняет крошечный размер исполняемых файлов и молниеносную загрузку: система не тратит время на инициализацию абстракций, слоёв совместимости и десятков фоновых служб.</p><p>Важная оговорка: несмотря на миф «всё на ассемблере», часть приложений и драйверов портирована или написана на C, Free Pascal, Oberon и даже Python. Но ядро, графическая подсистема и системные вызовы остаются ассемблерными.</p><h3>Файловые системы и загрузка</h3><p>KolibriOS умеет загружаться с CD/DVD, USB, жёстких дисков и даже с классической дискеты 1,44 МБ. Поддерживаются файловые системы FAT12/16/32, exFAT, NTFS и ext2/3/4. Есть варианты для Coreboot и загрузка прямо из Windows.</p><h2>Что внутри: программы и игры</h2><p>Несмотря на размер, KolibriOS поставляется с обширным набором софта. По данным сообщества, в образ входят более <b>250 программ</b>, включая текстовый процессор, просмотрщик изображений, графический редактор, музыкальный плеер, IRC-клиент и веб-браузеры.</p><p>Из игр и развлечений — порты эмуляторов, DOSBox, а также shareware-версии классики вроде DOOM и Wolfenstein 3D на полноценном CD-образе. Есть поддержка SDL, что позволяет переносить проекты с «больших» систем.</p><blockquote>Something this polished deserves a while to play around.</blockquote><h2>Главное ограничение: интернет без HTTPS</h2><p>Дженни Лист называет именно этот момент решающим: встроенные браузеры KolibriOS, включая Netsurf и Webview, не поддерживают HTTPS. Без этого современный веб практически закрыт: большинство сайтов, включая сам Hackaday, просто не откроются.</p><p>Это меняет статус системы с «основной ОС на каждый день» на «платформу для специальных задач». Почта, мессенджеры, облачные сервисы и онлайн-документация остаются недоступны без промежуточного прокси или другой машины.</p><p><b>Почему HTTPS сложно:</b><br />Современный TLS требует большой криптографической библиотеки, регулярных обновлений корневых сертификатов и поддержки множества алгоритмов. В условиях ОС размером в мегабайт это серьёзный архитектурный вызов, который сообщество пока не взяло в штатный комплект.</p><h2>Кому и где пригодится KolibriOS</h2><p>С учётом ограничений у KolibriOS остаётся несколько чётких сценариев применения. В России и соседних странах это особенно актуально: парки старого офисного и школьного оборудования часто содержат машины, которые официально «не тянут» современные ОС.</p><ul><li>Воскрешение старого железа. Pentium- и ранние Core-машины получают рабочий стол, браузер для локальных страниц и офисные утилиты.</li><li>Тонкие клиенты. Низкие требования к RAM и CPU делают KolibriOS кандидатом для терминалов с удалённым рабочим столом.</li><li>Обучение. Ядро на ассемблере, простые системные вызовы и небольшой кодовой базы — отличная площадка для изучения устройства ОС.</li><li>Ретрокомпьютинг. Запуск DOS-игр через эмулятор, классические 2D-игры и ностальгический интерфейс 1990-х.</li><li>Встроенные системы. Сборка Kolibri-A ориентирована на специализированное железо и железные проекты.</li></ul><h2>Как попробовать KolibriOS</h2><p>Попробовать систему проще всего в виртуальной машине. Скачайте ISO-образ с официального сайта, создайте VM с 64 МБ ОЗУ и загрузитесь с образа. Для реального железа подойдёт запись образа на USB-флешку или CD.</p><ul><li>Скачайте ISO или образ дискеты с официального сайта KolibriOS.</li><li>Для VM: создайте 32-битную машину с 64 МБ RAM и VESA-видео.</li><li>Для железа: запишите ISO на CD или USB через обычную утилиту.</li><li>Загрузитесь и выберите разрешение экрана из меню загрузчика.</li><li>Исследуйте меню «Пуск»: игры, утилиты, терминал и браузер.</li></ul><p>Если планируете использовать сетевые функции, проверьте совместимость сетевой карты по <a href="https://wiki.kolibrios.org/" rel="noopener noreferrer">списку оборудования</a>. Поддерживаются многие чипы Realtek, Intel и 3Com.</p><h2>Выводы</h2><p>KolibriOS 0.7.7 — это не замена Windows или Linux, а демонстрация того, какой отклик может давать компьютер, когда софт пишут под железо, а не под абстракции. В эпоху, когда операционки растут гигабайтами, проект напоминает: минимализм ещё жив.</p><p>Для российского читателя у KolibriOS есть дополнительный смысл: это открытый проект с русскоязычным сообществом, который можно изучать, дополнять и применять для решения практических задач — от перевода старого классного компьютера в рабочее состояние до обучения низкоуровневому программированию.</p><p>Главный вопрос — нужна ли вам такая система прямо сейчас. Если у вас есть старый 32-битный ноутбук, интерес к ассемблеру или просто ностальгия по быстрым ОС — попробуйте. А для повседневной работы с интернетом пока придётся держать под рукой что-то посовременнее.</p><p><b>Источники:</b></p><ul><li><a href="https://hackaday.com/2026/07/02/jennys-daily-drivers-kolibrios-0-7-7/" rel="noopener noreferrer">Jenny’s Daily Drivers: KolibriOS 0.7.7 — Hackaday</a></li><li><a href="https://distrowatch.com/kolibri" rel="noopener noreferrer">KolibriOS на DistroWatch</a></li><li><a href="https://wiki.kolibrios.org/" rel="noopener noreferrer">Официальная вики KolibriOS</a></li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Async Rust: как уменьшить бинарник, пока rustc не оптимизирован</title>
      <link>https://tproger.ru/articles/async-rust-kak-umenwit-binarnik-poka-rustc-ne-optimizirovan</link>
      <comments>https://tproger.ru/articles/async-rust-kak-umenwit-binarnik-poka-rustc-ne-optimizirovan?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/async-rust-kak-umenwit-binarnik-poka-rustc-ne-optimizirovan</guid>
      <description><![CDATA[<p>Разбираем, почему async Rust генерирует лишние state machine, как это бьёт по embedded и WASM, и что можно сделать в коде прямо сейчас.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/async-rust-kak-umenwit-binarnik-poka-rustc-ne-optimizirovan">Async Rust: как уменьшить бинарник, пока rustc не оптимизирован</a>»</p>]]></description>
      <category><![CDATA[Многопоточность]]></category>
      <category><![CDATA[Низкоуровневое программирование]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Компиляторы]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Асинхронное программирование]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 29 Jun 2026 10:21:42 GMT</pubDate>
      <content:encoded><![CDATA[<p>Асинхронный Rust обещал нам zero-cost abstraction: пишете почти как на обычном синхронном языке, а runtime сам разбирается с конкурентностью. На сервере с десятками гигабайт RAM это обещание обычно сбывается. Но стоит перейти к embedded-прошивке на 256 КБ flash или к WebAssembly-модулю, который гонится за каждым килобайтом, — и картина меняется. Функция с двумя await-точками превращается в state machine из 360 строк MIR, тогда как синхронный аналог укладывается в 23. Почему так происходит и что с этим делать уже сегодня?</p><p>Тема получила новый импульс после серии публикаций Диона Доктера (Dion Dokter) из голландской компании Tweede golf. Во второй части он заглядывает внутрь rustc и утверждает: async Rust так и не вышел из состояния MVP. Я не буду дословно переводить его рассуждения, а переосмыслю их с упором на практику: как понять, платите ли вы за async-удобство лишними байтами, и как снизить налог, пока компилятор не научился делать это сам.</p><ul><li>Асинхронный Rust компилируется в state machine: на каждую await-точку приходится отдельное состояние плюс служебные Unresumed, Returned и Panicked.</li><li>Простая функция с двумя await-точками даёт 360 строк MIR против 23 у синхронного кода. Часть излишков LLVM убирает, но не все, особенно при оптимизации по размеру.</li><li>Компилятор не схлопывает идентичные состояния, не инлайнит futures через await, генерирует state machine даже для async-блоков без await и оставляет panic-путь в состоянии Returned.</li><li>В release-сборках замена panic в Returned на возврат Pending может сократить прошивку на 2–5%. Убирание state machine у пустых async-блоков — ещё около 0,2%.</li><li>Пока rustc не исправлен, можно вручную: возвращать impl Future из «прозрачных» функций, использовать std::future::ready, схлопывать await-точки через match и передавать большие данные по ссылке.</li></ul><h2>Почему async-функция — это всегда state machine</h2><p>Когда вы пишете async fn, компилятор не просто оборачивает тело в callback. Он строит конечный автомат: enum, где каждый вариант — состояние между await-точками. Это называется coroutine desugaring и происходит на уровне MIR (Mid-level IR) — до того, как код попадает в LLVM.</p><p>Рассмотрим минимальный пример. Есть две функции, каждая из которых просто возвращает число, и третья складывает результаты:</p><p>У bar две await-точки, поэтому минимум два пользовательских состояния. Но если сдампить MIR после прохода coroutine_resume, картина шире:</p><p>Три служебных состояния добавляются всегда. Они нужны, чтобы соблюсти контракт Future::poll: опрос завершённой future не должен приводить к неопределённому поведению. Поэтому после первого Ready future переходит в Returned, а повторный poll паникует. Аналогично Panicked блокирует future после пойманного panic — похоже на poisoning мьютекса.</p><p>Логика корректная, но цена высокая: bar порождает 360 строк MIR, а эквивалентный синхронный код — 23. В 15 с лишним раз больше. LLVM часть этого выбросит, но на opt-level = "z" или в толстых async-графах вызовов он быстро сдаётся.</p><h2>Четыре места, где компилятор работает вхолостую</h2><h3>1. Panic в состоянии Returned</h3><p>Контракт future требует только отсутствия UB. Паниковать при повторном poll — не обязательно. Можно просто вернуть Pending снова: ничего опасного не произойдёт, а ветка panic — это лишний побочный эффект, который плохо оптимизируется.</p><p>Дион сделал экспериментальный патч rustc: в release-режиме Returned больше не паникует. На embedded-прошивках это дало <b>2–5% экономии бинарного размера</b>. В debug-сборках панику можно оставить, чтобы быстро ловить ошибочный повторный poll, — по аналогии с overflow-checks.</p><h3>2. State machine без await</h3><p>Взгляните на функцию без единой await-точки:</p><p>Идеальная реализация — всегда возвращать Poll::Ready(5) безо всякого enum. А rustc генерирует полноценный CoroutineLayout с тремя служебными состояниями и switch по дискриминанту. Такие функции часто появляются в трейтах, где один интерфейс должен быть async, но конкретная реализация ничего не ждёт. Патч, убирающий state machine у async-блоков без await, даёт ещё <b>0,2%</b> размера — мало, но правка тривиальная.</p><h3>3. Futures не инлайнятся через await</h3><p>Классический шаблон: адаптер просто пересылает вызов вниз.</p><p>Сейчас bar получает собственный state machine, который вызывает state machine foo. Вручную мы бы написали fn bar(blah) -&gt; impl Future { foo(blah) } и избавились бы от лишнего enum. Аналогично с преамбулой и постамбулой: их можно перенести на FutureExt::map из крейта futures.</p><h3>4. Одинаковые состояния не схлопываются</h3><p>Если в async-функции несколько веток match ведут к одинаковому await, компилятор создаёт отдельное состояние на каждую ветку:</p><p>Здесь send_response вызывается с разными аргументами, но структура состояний дублируется. Если вынести выбор аргумента за await, получится одно состояние вместо двух:</p><p>В одном примере из статьи MIR сокращается с 456 до 302 строк, а ассемблер — примерно на 11%. Оптимизации stack, поэтому выигрыш в реальном коде может быть заметнее.</p><h2>Что делать сегодня, не дожидаясь rustc</h2><p>Всё перечисленное выше — это работа для команды компилятора. Но релиз с этими патчами может занять месяцы, а то и годы. Пока он не вышел, можно снизить async-bloat в своём коде.</p><p><b>Главный принцип:</b> каждый лишний async fn — это лишний state machine. Если функция не содержит await, не делайте её async.</p><h3>Заменяйте async-пустышки на impl Future</h3><p>Типичный случай — трейт, где одна реализация реально ждёт ввода-вывода, а другая просто возвращает значение.</p><p>Вместо полноценного state machine получается готовая future, которая сразу возвращает Ready. Это особенно полезно в embedded-HAL, где трейты часто async, но конкретный драйвер может делать только прямой доступ к регистрам.</p><h3>Прозрачные обёртки без await</h3><p>Если функция только пересылает await вниз, уберите await:</p><h3>Схлопывайте await-точки</h3><p>Если несколько веток match заканчиваются одним и тем же await, вынесите выбор аргументов наружу. Это уменьшает число состояний state machine и облегчает жизнь LLVM.</p><h3>Передавайте большие данные по ссылке</h3><p>Async-функция захватывает в state machine всё, что живёт через await. Если передать массив по значению, он окажется внутри future целиком:</p><p>Разница в 52 раза по размеру future — и это без учёта memcpy, который компилятор вынужден вставлять при перемещении больших значений.</p><h2>Перспективы: Project Goal и финансирование</h2><p>Дион оформил эти идеи как <a href="https://rust-lang.github.io/rust-project-goals/2026/async-statemachine-optimisation.html" rel="noopener">Rust Project Goal</a> — формальный механизм, через который команды заявляют цели на полугодие. По его оценке, работа требует порядка <b>€30 000</b> финансирования. Для компиляторного проекта это скромная сумма: речь идёт о 2–5% размера прошивки практически в любом async-проекте.</p><p>Есть и другой подход к той же проблеме: не убирать state machine на входе, а научить LLVM лучше их оптимизировать на выходе. Дион считает, что оба направления дополняют друг друга. Чем проще state machine попадает в LLVM, тем эффективнее её можно проинлайнить и упростить на поздних проходах.</p><h2>Выводы</h2><blockquote>Async Rust never left the MVP state. The compiler generates state machines with a lot of unnecessary baggage. With a few targeted optimizations in rustc we can get smaller binaries and better performance for everyone.</blockquote><p>Утверждение «async Rust так и не вышел из MVP» звучит резко, но поясняет, почему наши «zero-cost» абстракции иногда всё-таки стоят дорого. Компилятор делает корректный, но не оптимальный код: лишние panic-ветки, неинлайненные futures, дублирующиеся состояния и state machine там, где они не нужны.</p><p>Для embedded и WASM это не абстрактная проблема, а конкретные килобайты прошивки. Пока rustc учится, разработчик может снизить налог вручную: убирать async у функций без await, возвращать impl Future из прозрачных обёрток, схлопывать await-точки и не передавать большие структуры по значению.</p><p>Если ваш проект на Rust бьётся о лимит flash, имеет смысл посмотреть на async-граф вызовов свежим взглядом. Часто проще убрать одну лишнюю async-обёртку, чем месяцами ждать патч в компиляторе.</p><p><b>Источники:</b><br />— <a href="https://tweedegolf.nl/en/blog/237/async-rust-never-left-the-mvp-state" rel="noopener">Dion Dokter, «Async Rust never left the MVP state»</a>;<br />— <a href="https://tweedegolf.nl/en/blog/235/debloat-your-async-rust" rel="noopener">Dion Dokter, «Debloat your async Rust»</a>;<br />— <a href="https://rust-lang.github.io/rust-project-goals/2026/async-statemachine-optimisation.html" rel="noopener">Rust Project Goal: Async state machine optimisation</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как я ужал NixOS ISO с 458 МБ до 183 МБ</title>
      <link>https://tproger.ru/articles/kak-ya-uzhal-nixos-iso-s-458-mb-do-183-mb</link>
      <comments>https://tproger.ru/articles/kak-ya-uzhal-nixos-iso-s-458-mb-do-183-mb?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-ya-uzhal-nixos-iso-s-458-mb-do-183-mb</guid>
      <description><![CDATA[<p>Разбираем эксперимент: отключаем Nix, SSH, GRUB, модули ядра и Perl-активацию. Уменьшаем ISO NixOS почти в 2,5 раза и объясняем, почему это не для продакшена.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-ya-uzhal-nixos-iso-s-458-mb-do-183-mb">Как я ужал NixOS ISO с 458 МБ до 183 МБ</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Операционные системы]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 29 Jun 2026 04:39:16 GMT</pubDate>
      <content:encoded><![CDATA[<p>Базовый установочный ISO <b>NixOS</b> весит <b>458 МБ</b> — и в нём даже нет vim. Для сравнения, Alpine Linux укладывается примерно в <b>66 МБ</b>. Разбираем, как автор блога natkr.com уменьшил NixOS почти в 2,5 раза, и где здесь подвох.</p><h2>Что такое NixOS</h2><p><b>NixOS</b> — это дистрибутив Linux, в котором всё системное окружение описывается декларативно на языке <b>Nix</b> и воспроизводится из одного конфигурационного файла. Каждая сборка помещает зависимости в /nix/store, что даёт атомарные откаты и воспроизводимость, но при этом легко разрастается размер.</p><p>Одна из удобных фич — команда nixos-rebuild build-vm: она превращает конфигурацию в виртуальную машину. Но иногда нужен не «тонкий» образ, привязанный к хосту, а самодостаточный ISO, который можно записать на флешку или загрузить в облаке.</p><ul><li>Базовый NixOS ISO занимает 458 МБ: 416 МБ — squashfs с пользовательским окружением, 26 МБ — initrd, 13 МБ — ядро.</li><li>Главные «тяжеловесы» внутри образа: модули ядра (144 МБ), Python 3 (128 МБ), systemd (60 МБ), Perl (56 МБ), GRUB (около 62 МБ).</li><li>Отключение Nix, документации, SSH, firewall и лишних зависимостей сокращает ISO до 197 МБ.</li><li>Замена Perl-активации на экспериментальные system.etc.overlay и services.userborn даёт финальный размер около 183 МБ.</li><li>Это игрушечный эксперимент: для десктопа и сервера такие отсечения небезопасны, но полезны как источник идей.</li></ul><h2>От VM к ISO за несколько строк</h2><p>Минимальная виртуальная машина на NixOS собирается из файла примерно такого вида:</p><p>Запуск: $(nix-build basic-vm.nix --attr vm --no-out-link)/bin/run-nixos-vm. При этом /nix/store монтируется с хоста, поэтому VM «тонкая». Для независимого ISO импортируем модуль iso-image.nix и собираем атрибут isoImage:</p><p>ISO собирается, загружается в QEMU, но размер сразу бросается в глаза.</p><h2>458 МБ — из чего они сложились</h2><p>Автор примонтировал полученный ISO и посмотрел распределение места. Вот ключевые цифры:</p><ul><li><b>416 МБ</b> — сжатый nix-store.squashfs с пользовательским окружением.</li><li><b>26 МБ</b> — начальная загрузочная среда initrd.</li><li><b>13 МБ</b> — ядро Linux.</li><li><b>3 МБ</b> — загрузчик isolinux.</li></ul><p>При распаковке squashfs картина ещё интереснее:</p><ul><li><b>144 МБ</b> — модули ядра (linux-6.18.35-modules).</li><li><b>128 МБ</b> — Python 3.13.</li><li><b>60 МБ</b> — systemd.</li><li><b>56 МБ</b> — Perl.</li><li><b>~62 МБ</b> суммарно — две копии GRUB (UEFI + BIOS).</li></ul><p>Поскольку ISO собирается локально, все пакеты из него есть и в хостовом /nix/store. Это позволяет использовать nix why-depends, чтобы найти, кто тащит каждую зависимость.</p><h2>Отключаем Nix, документацию и лишние сервисы</h2><p>Первый очевидный шаг — отказаться от демона Nix внутри ISO, ведь мы строим автономный образ, а не полноценную NixOS для работы с пакетами. Достаточно двух опций:</p><p>Результат: <b>384 МБ</b>. Экономия скромная, потому что часть зависимостей Nix всё ещё тянется через сервис register-nix-paths. Он регистрирует содержимое хранилища ISO при загрузке — но Nix-то мы уже убрали. Отключаем и его:</p><p>Теперь ISO уменьшился до <b>360 МБ</b>, а зависимость от Boost исчезла полностью.</p><h2>SSH: модуль без выключателя</h2><p>Следующий кандидат на удаление — OpenSSH-клиент. Проблема в том, что модуль programs/ssh.nix добавляет его в environment.corePackages, а отдельного переключателя programs.ssh.enable нет.</p><p>Попытка полностью исключить модуль через disabledModules ломает другие модули, например Plasma 6, которые ожидают существования опций programs.ssh. Выход — подменить опцию «пустышкой», не затрагивающей реальную конфигурацию:</p><p>Параллельно автор убрал firewall, documentation.man.enable и принудительно очистил environment.defaultPackages.</p><h2>GRUB: зачем две копии загрузчика</h2><p>ISO-пресет NixOS включает сразу и UEFI-, и BIOS-версии GRUB — отсюда около 62 МБ. Чёткого флага для отключения одной из них нет, поэтому автор пошёл грубым путём: сбросил system.extraDependencies и environment.systemPackages, оставив только environment.corePackages, чтобы shell хотя бы стартовал.</p><h2>Ядерные модули: четверть образа</h2><p>Модули ядра весили <b>144 МБ</b> — больше, чем весь Alpine ISO. NixOS не предоставляет удобного способа ограничить их набор для runtime, поэтому автор просто удалил папку модулей из системы после сборки:</p><p>Это полностью отключает динамическую загрузку модулей. Если что-то нужно — оно должно оказаться в boot.initrd.kernelModules или availableKernelModules. После этого образ уменьшился до <b>197 МБ</b>.</p><h2>Perl: заменяем активацию системы</h2><p>Perl в образе нужен только для скриптов активации: настройки /etc и создания пользователей. В nixpkgs уже есть экспериментальные замены — system.etc.overlay и нативный менеджер пользователей userborn.</p><p>Оба механизма помечены как экспериментальные, но в контексте «самодельного минимального ISO» это уже не самая безумная идея. Финальный размер: <b>183 МБ</b>.</p><h2>Что получилось и стоит ли повторять</h2><p>За несколько итераций образ сократился с <b>458 МБ</b> до <b>183 МБ</b> — почти в 2,5 раза. При этом система всё ещё загружается, автор смог залогиниться под root, правда разрешение экрана перестало переключаться.</p><p><b>Важно:</b> конфигурация выше — это не рецепт для продакшена. Отсутствие Nix, SSH, модулей ядра и Perl-активации ломает массу сценариев. Результат интересен скорее как демонстрация границ NixOS, чем как готовый шаблон.</p><p>Автор сама отмечает: для рабочего десктопа или сервера так делать не стоит. Но если вам нужен крошечный live-образ под единичный эксперимент — здесь много идей, с которых можно начать.</p><h2>Выводы</h2><p>Эксперимент показывает, что NixOS позволяет не только собирать сложные системы, но и жёстко урезать их — иногда в ущерб удобству. Главный инструмент здесь не волшебная опция, а последовательный анализ: смотреть, что занимает место, находить виновника через nix why-depends и решать, готовы ли вы от него отказаться.</p><blockquote>«At some point I just kept going because I got curious.»</blockquote><p>Если захотите повторить — начните с отключения Nix и документации, а дальше решайте, какие модули и пакеты действительно нужны в вашем live-образе. Исходный материал — в блоге автора: <a href="https://natkr.com/2026-06-19-nixos-but-smol/">I can haz smoller NixOS ISOs?</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>В Linux 7.2 улучшили EROFS для разреженных AI-датасетов и эффективного ввода-вывода</title>
      <link>https://tproger.ru/news/v-linux-7-2-uluchwili-erofs-dlya-razrezhennyh-ai-datasetov-i-effekt</link>
      <comments>https://tproger.ru/news/v-linux-7-2-uluchwili-erofs-dlya-razrezhennyh-ai-datasetov-i-effekt?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/v-linux-7-2-uluchwili-erofs-dlya-razrezhennyh-ai-datasetov-i-effekt</guid>
      <description><![CDATA[<p>В ядре Linux 7.2 файловая система EROFS получила поддержку разреженных данных и ускорение I/O. Разбираем, зачем это нужно для AI-моделей.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/v-linux-7-2-uluchwili-erofs-dlya-razrezhennyh-ai-datasetov-i-effekt">В Linux 7.2 улучшили EROFS для разреженных AI-датасетов и эффективного ввода-вывода</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Операционные системы]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 25 Jun 2026 08:58:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>В ядро <b>Linux 7.2</b> вошли обновления файловой системы <b>EROFS</b>, которые делают её удобнее для хранения больших разреженных наборов данных — например, датасетов для обучения ИИ. Главное изменение — поддержка SEEK_HOLE в компоновке физических кластеров (pcluster), что позволяет сохранять «дыры» в файлах вместо их сжатия в нули.</p><p><b>EROFS</b> (<i>Enhanced Read-Only File System</i>) — это доступная с Linux 4.19 файловая система только для чтения, заточенная под производительность и компактное хранение образов контейнеров, систем Android и, всё чаще, AI-моделей.</p><ul><li>Linux 7.2 получил два ключевых улучшения EROFS: разреженные pcluster и оптимизированное отображение chunk-based inode.</li><li>Поддержка sparse-данных помогает экономить место при копировании в overlayfs и корректно работать с SEEK_HOLE.</li><li>Удалён устаревший бэкенд FSCACHE — его функциональность заменяют файловые монтирования и fanotify.</li><li>Патчи подготовил мейнтейнер EROFS Гао Сян из Alibaba.</li></ul><h2>Зачем нужна поддержка разреженных данных</h2><p>Разреженные (sparse) файлы содержат пустые участки, которые не занимают место на диске. EROFS уже умеет прозрачно сжимать нули, но для AI-датасетов важно именно <b>сохранять дыры</b>: иначе overlayfs при копировании «вверх» распределит под них реальное пространство, а системный вызов SEEK_HOLE не найдёт ни одной «дыры».</p><p>Мейнтейнер EROFS Гао Сян из Alibaba <a href="https://lore.kernel.org/all/20260621194414.489939-1-hsiangkao@linux.alibaba.com/">пояснил в патчах</a>, что новый код отмечает целые pcluster как «дыры» двумя разными способами. Это особенно полезно для огромных AI-датасетов, где нулевые или неинициализированные фрагменты встречаются часто.</p><h2>Оптимизация ввода-вывода и удаление FSCACHE</h2><p>Помимо sparse-поддержки, EROFS в Linux 7.2 получила оптимизированное отображение запросов для inode на основе чанков. Разработчики сообщают, что новый код erofs_map_chunks() делает ввод-вывод более эффективным, хотя точных бенчмарков в анонсе не приводится.</p><p>Также из ядра окончательно удалили бэкенд <b>FSCACHE</b> для EROFS. Он раньше использовался для ленивой подгрузки образов, но после того как FSCACHE сделал NETFS обязательной зависимостью, функцию признали устаревшей. Похожее поведение теперь реализуют через монтирование файлов и хуки fanotify.</p><h2>Итог</h2><p>Обновления EROFS в Linux 7.2 делают эту файловую систему более удобной для больших AI-датасетов: разреженные данные теперь не превращаются в лишние мегабайты при копировании, а ввод-вывод для chunk-based inode стал эффективнее. Для разработчиков, которые используют EROFS в контейнерных и ML-окружениях, это повод проверить свои сборки ядра.</p><p>Источник: <a href="https://www.phoronix.com/news/EROFS-Sparse-AI-Datasets">Phoronix</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как Android-инженер спроектировал gateway для миллионов пользователей: опыт перехода в инфраструктуру</title>
      <link>https://tproger.ru/articles/kak-android-inzhener-sproektiroval-gateway-dlya-millionov-polzova</link>
      <comments>https://tproger.ru/articles/kak-android-inzhener-sproektiroval-gateway-dlya-millionov-polzova?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Кирилл Соколов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-android-inzhener-sproektiroval-gateway-dlya-millionov-polzova</guid>
      <description><![CDATA[<p>История перехода из андроид разработки в инфраструктуру. Как мобильный инженер спроектировал gateway для платформы с миллионами пользователей, освоил распределённые системы и научился строить отказоустойчивые сервисы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-android-inzhener-sproektiroval-gateway-dlya-millionov-polzova">Как Android-инженер спроектировал gateway для миллионов пользователей: опыт перехода в инфраструктуру</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Android]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[App Store]]></category>
      <category><![CDATA[Игра]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 23 Jun 2026 07:41:54 GMT</pubDate>
      <content:encoded><![CDATA[<p><i>Перешёл из Android-разработки в инфраструктуру и спроектировал gateway для платформы с миллионами пользователей. Делюсь опытом: какие пробелы пришлось закрывать, почему мобильный бэкграунд — это преимущество, и с чего начать, если думаете о похожем переходе. </i></p><h2>Почему инфраструктура начинает привлекать больше, чем фичи</h2><p>С фичами всё прозрачно: написал код — увидел результат на экране. Быстрая и понятная обратная связь. Но со временем замечаешь, что проблемы повторяются. Приложение тормозит не из-за плохого кода, а потому что на один экран уходит пять-шесть сетевых вызовов. Логика на клиенте. Хочешь что-то изменить — готовь релиз, проходи App Store Review и жди недели, пока обновление дойдёт до всех.</p><p>Я перешёл в Android-инфраструктуру — начал делать инструменты для других мобильных разработчиков. Это помогло увидеть: главные проблемы не в фичах, а в слое между приложением и бэкендом.</p><p>Возвращаться к фичам стало неинтересно. В инфраструктуре задачи сложнее, результат измеряется метриками — latency, error rate, скорость релизов, — а влияние на всю систему, а не на один экран.</p><h2>Что Android даёт для инфраструктуры — а чему учиться с нуля</h2><p>Мобильный бэкграунд оказался не балластом, а преимуществом. Я понимал ограничения изнутри. Backend-инженер может прочитать, что мобильные сети ненадёжны, память ограничена, а батарея — критичный ресурс. Но прочитать и прочувствовать — разное. Я годами наблюдал, как приложение “захлёбывается” на устройствах среднего сегмента. Знал, что 60% пользователей сидят именно на таких. Видел, как баг, который мы починили за день, продолжает висеть у людей неделями — просто потому, что они не успели обновиться.</p><p>Когда я проектировал gateway, я точно знал, что почувствуют мобильные разработчики, если ошибусь. Добавить ещё один сетевой вызов — это будет не бесплатно. Оставить логику в приложении — значит отдать её на устройство, которое я не контролирую.</p><p><b>Чего именно не хватало?</b> Я неплохо понимал мобильную сторону, но совершенно не ориентировался в распределенных системах. Знал, например, что такое таймаут, но не представлял, как выставить его в цепочке из пяти сервисов так, чтобы одно медленное звено не обрушило весь экран пользователя. Понимал, что сети падают, но не умел проектировать систему, способную оставаться на плаву в таких условиях.</p><p><b>Чему пришлось учиться с нуля? </b>Операционному мышлению. В Android ты выпускаешь релиз — и он либо работает, либо нет. Если крашится, починишь в следующей версии. В инфраструктуре нет «следующей версии». Если gateway падает, всё приложение ложится для миллионов пользователей прямо сейчас.</p><p>Пришлось учиться думать в терминах деградации, частичных отказов, плавного падения.</p><p>Что делать, если один из пяти сервисов не ответил? Как понять, что мы катимся к инциденту, до того, как пользователи начнут жаловаться?</p><p>Этому в мобильной разработке не учат.</p><h2>Как я учился: пробелы, сроки и смена мышления</h2><p>Формального плана у меня не было — учился на практике. Это лучший, хотя и самый стрессовый способ. Пробелы выявляла практика. Столкнулся с нерешаемой задачей — понял, чего не знаю. Пошёл разбираться.</p><p>Учился итеративно, не пытаясь объять необъятное сразу. Gateway начинался как простой прокси. Затем добавили агрегацию ответов, потом — конфигурационные определения экранов. Каждый такой шаг вынуждал осваивать следующий уровень: circuit breakers, стратегии повторов, observability, планирование мощностей.</p><p>По срокам: техническая база уложилась в несколько месяцев. Паттерны осваиваются быстрее, чем кажется, особенно если сразу применять их к живой задаче. Гораздо дольше происходила смена образа мышления. Перейти от вопроса «работает ли фича?» к вопросу «что случится, когда это упадёт в три часа ночи?» — вот что заняло основное время.</p><h2>Что означает «выдающийся уровень» в инфраструктуре</h2><p><i>Когда говорят «спроектировать gateway с нуля и перевести на него живую платформу», за этими словами стоит не один навык, а целых три, и каждый требует совершенно разной подготовки.</i></p><p>Проектирование с нуля — это не рисование квадратиков на доске и не выбор модного стека. Это в первую очередь определение границ: что система будет делать, а что — категорически нет, и как с ней станут взаимодействовать десятки команд. Настоящая сложность здесь в том, чтобы предвидеть, что именно сломается, и заложить защиту от этого ещё до того, как написан хоть один файл с кодом.</p><p>Затем — миграция живой системы, где права на ошибку практически нет. Приложение нельзя выключить или отрепетировать в реальном масштабе. Остаётся только постепенный перевод трафика: shadow mode → 1% → 5% → 25% → 50% → 100%, с автоматическим откатом при любом росте ошибок. И всё это — пока миллионы пользователей активно работают с продуктом, не подозревая, что под капотом идёт замена двигателя на ходу. Такой уровень дисциплины и инструментации приходит только с практикой.</p><p>Наконец, владение надёжностью. Gateway — единая точка отказа: упал он, упало всё. Годы уходят на то, чтобы сделать его скучным и предсказуемым: резервирование, автомасштабирование, circuit breakers, режимы деградации, еженедельный пересмотр мощностей. Высший пилотаж — когда о системе просто не думаешь, потому что она работает.</p><h2>Почему путь в инфраструктуру доступнее, чем кажется?</h2><p>Карьерные траектории в инфраструктуре редко бывают чётко описаны. Здесь нет готового чек-листа в духе «диплом по Computer Science, пять лет в бэкенде, обязательное знание Kafka и Kubernetes». С одной стороны, такая неопределённость пугает. С другой — именно она и делает этот путь более доступным, чем принято думать.</p><p>Когда перед тобой лежит жёсткий список формальных требований, люди часто отсеивают себя сами, даже не попробовав. А в инфраструктуре по-настоящему важно только одно: можешь ли ты решать задачи. Я пришёл сюда без профильного диплома и учился ровно тому, что требовалось в моменте, потому что задачи сами подталкивали к этому.</p><p>Индустрия, к слову, до сих пор не слишком хорошо умеет проверять те навыки, которые на этом уровне оказываются решающими: умение видеть ограничения на стыке систем, предвидеть сценарии отказов, двигать людей к соглашению. Всему этому учатся не до начала работы, а непосредственно в процессе.</p><p>Поэтому если вы мобильный инженер и размышляете, можно ли перейти в инфраструктуру, — вопрос не в том, правильный ли у вас бэкграунд. Вопрос в другом: готовы ли вы учиться тому, чего пока не знаете, и способны ли обратить то, что уже понимаете, в собственное преимущество. Если ответ «да» — путь для вас открыт. Просто указателей на нём пока не расставили.</p><h2>Мобильный бэкграунд как преимущество архитектора</h2><p>Считаю ли я, что мобильный опыт сделал меня лучшим архитектором для mobile-first продуктов? Безоговорочно, да.</p><p>Я помнил, как ощущается медленный экран на устройстве среднего сегмента. Помнил, что случается, когда API возвращает слегка неправильные данные и приложение падает при парсинге. Помнил то чувство, когда баг уже в проде, а ты ждёшь App Store Review и ничего не можешь исправить.</p><p>Поэтому когда я проектировал gateway, я не занимался абстрактной «оптимизацией перформанса». Я опирался на совершенно конкретный опыт. Знал, что убрать один сетевой round trip — это подарок каждому мобильному разработчику. Знал, что перенос логики на сервер означает перенос в место, где я могу починить всё за минуты, а не за недели.</p><p>Лучшая инфраструктура для мобильных продуктов строится теми, кто сам их создавал и знает все узкие места не понаслышке. Этот опыт даёт верное направление: ты чувствуешь, где настоящие проблемы, потому что сталкивался с ними лично. Такому не учат по книгам.</p><h2>Коротко: что делать, если думаете о переходе</h2><ul><li>Найдите промежуточный шаг. Не прыгайте сразу в бэкенд. Начните с задач на стыке: оптимизация API, инструменты для мобильных разработчиков, улучшение сетевого слоя.</li><li>Используйте мобильный контекст как рычаг. Вы понимаете то, о чём бэкенд-инженеры только догадываются. Говорите об этом вслух.</li><li>Учитесь измерять невидимое. В инфраструктуре результат — это метрики: latency, error rate, скорость релизов. Учитесь рассказывать историю через цифры.</li><li>Проектируйте под отказ, а не тушите пожары. Senior-уровень — это определить, что сломается и кто за это отвечает, до того, как оно сломается.</li><li>Не ждите разрешения. Путь не размечен, но он открыт. Начните с малого — и двигайтесь туда, где задачи становятся интереснее.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Что такое Git worktrees и зачем их использовать</title>
      <link>https://tproger.ru/translations/chto-takoe-git-worktrees-i-zachem-ih-ispolzovat</link>
      <comments>https://tproger.ru/translations/chto-takoe-git-worktrees-i-zachem-ih-ispolzovat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/chto-takoe-git-worktrees-i-zachem-ih-ispolzovat</guid>
      <description><![CDATA[<p>Git worktrees появились ещё в 2015 году, но популярность обрели только недавно. Разбираем, что это такое, как использовать в терминале и зачем они пригодятся.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/chto-takoe-git-worktrees-i-zachem-ih-ispolzovat">Что такое Git worktrees и зачем их использовать</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Инструменты командной строки]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 18 Jun 2026 03:00:07 GMT</pubDate>
      <content:encoded><![CDATA[<p>Перевод статьи Cassidy Williams из GitHub Blog, оригинал: <a href="https://github.blog/ai-and-ml/github-copilot/what-are-git-worktrees-and-why-should-i-use-them/">https://github.blog/ai-and-ml/github-copilot/what-are-git-worktrees-and-why-should-i-use-them/</a></p><p>Git worktrees позволяют держать несколько рабочих копий одного репозитория на разных ветках — и сейчас эта возможность в тренде. Забавно, что в Git она появилась ещё в 2015 году. Но worktrees действительно удобны, и в этой статье разберёмся, зачем они нужны, чем отличаются от обычных веток и почему внезапно стали популярны.</p><p>worktree — это дополнительная рабочая копия репозитория на другой ветке, привязанная к тому же .git.</p><p>С их помощью можно переключаться между задачами, не прерывая текущую работу и не используя stash.</p><p>Они особенно полезны при параллельной работе с ИИ-агентами и в приложении GitHub Copilot.</p><p>У worktrees есть ограничения: раздувание зависимостей, необходимость убирать папки, правила .gitignore и запрет на одновременный checkout одной ветки в нескольких worktrees.</p><h2>Переключение контекста через ветки и stash</h2><p>Представьте: вы работаете над задачей и вдруг получаете срочный баг. Нужно срочно переключить контекст.</p><p>Сначала, скорее всего, спрячете текущие изменения в stash:</p><p>Потом перейдёте на main и обновите её:</p><p>Затем создадите ветку с хотфиксом:</p><p>Пофиксите, закоммитите и запушите ветку:</p><p>После слияния pull request вы вернётесь к компьютеру, подтянете main и удалите ветку с багфиксом:</p><p>А потом сможете вернуться к задаче, над которой работали:</p><p>Фух. На чём мы остановились?</p><p>Переключение туда-сюда, перезагрузка файлов, переустановка node_modules в зависимости от того, что изменилось, — всё это отнимает много сил. Нагрузка от смены контекста серьёзная.</p><p>Это базовый пример, но иногда разработчики справлялись с таким хаосом сложными командами git stash или даже несколькими клонами одного репозитория (я сама грешила этим).</p><p>А потом появились… worktrees!</p><h2>Переключение контекста через worktrees</h2><p>С worktrees вы никогда не покидаете свою ветку и не используете stash, а редактор с вашей текущей фичей остаётся нетронутым.</p><p>Эта команда мгновенно создаёт соседнюю папку hotfix-workspace, базирует её на main и создаёт новую ветку hotfix-bug.</p><p>Теперь можно открыть эту папку в новом окне редактора (или перейти в неё через cd) и чинить баг. Исходное окно редактора остаётся в том же состоянии, в котором вы его оставили.</p><p>Pull request сливаете онлайн, как обычно, а после слияния можно просто удалить временную папку.</p><p>Гораздо плавнее! Нет риска конфликтов stash, редактор не перезагружается, и вы действительно можете работать параллельно.</p><h2>Так почему же сейчас?</h2><p>Долгое время worktrees были относительно неизвестны. Большинство разработчиков никогда о них не слышали, потому что либо Git GUI не поддерживали их (или относились как к второсортной функции), либо все привыкли к знакомой схеме: feature-ветка, работа, PR, merge и повтор.</p><p>Сейчас наша работа изменилась. ИИ заставляет нас работать параллельно больше, чем когда-либо в истории разработки ПО. Разработчики запускают множество сессий одновременно, а «культура код-ревью» растёт быстрее, чем «культура написания кода».</p><p>Агенты и люди могут делать больше параллельно с помощью worktrees. Это режим по умолчанию в приложении GitHub Copilot и во многих других современных инструментах.</p><blockquote>Агенты и люди могут делать больше параллельно с помощью worktrees.</blockquote><h2>В чём подвох?</h2><p>Worktrees решают кучу проблем, но есть нюансы, на которые стоит обращать внимание.</p><ul><li>Раздувание зависимостей: каждая папка worktree требует собственную копию зависимостей проекта. Если запускать npm install или pip install в нескольких worktrees, диск может быстро закончиться.</li><li>Управление папками: нужно удалять папки worktree, чтобы со временем не засорять родительский каталог. Приложение GitHub Copilot часто делает это за вас, но если вы работаете в терминале, придётся следить самостоятельно.</li><li>Требования к глобальному .gitignore: если создавать worktree внутри основного репозитория, их нужно вручную добавить в .gitignore, чтобы случайно не закоммитить. Можно создавать worktree за пределами основного репозитория (GitHub Copilot делает это по умолчанию), но это стоит учитывать.</li><li>Ограничение «одна ветка»: Git не позволяет одновременно checkout’ить одну и ту же ветку в двух разных worktrees, чтобы избежать повреждения данных.</li></ul><h2>Как использовать git worktrees в приложении GitHub Copilot?</h2><p>Отличный вопрос! Здорово, что там всё работает «из коробки». Когда вы открываете приложение, на главном экране есть выпадающий список, который спрашивает, где запускать новую сессию. По умолчанию выбран новый worktree.</p><p>Когда вы запускаете новую сессию, можно нажать на имя сессии вверху приложения и увидеть (забавное!) сгенерированное имя вашего worktree, а также путь, где он находится, проект, для которого он создан, и сведения о внесённых изменениях.</p><p>Проще простого!</p><h2>Стоит ли использовать worktrees?</h2><p>Я дам вам самый senior-ответ, который только можно: зависит от ситуации! Вам может быть удобнее работать по-другому. Возможно, вы не так много работаете параллельно и привыкли к ментальной модели веток и stash. Возможно, теперь вы будете использовать только worktrees. А может, захотите и то, и другое!</p><p>Мир у ваших ног, и попробовать всё это можно уже сегодня в приложении GitHub Copilot.</p><p><b>Об авторе.</b> Cassidy Williams — старший директор по адвокации разработчиков в GitHub. Она создаёт ПО, консультирует стартапы и учит разработчиков строить лучше. Подписаться на её еженедельную рассылку можно на <a href="https://cassidoo.co/newsletter">cassidoo.co/newsletter</a>.</p><h2>Выводы</h2><p>Git worktrees — способ работать с несколькими ветками одновременно, не прерывая текущую задачу и не рискуя запутаться в stash. Они особенно удобны при параллельной работе с ИИ-агентами и встроены в приложение GitHub Copilot по умолчанию. Попробуйте — возможно, именно они упростят ваш рабочий процесс.</p>]]></content:encoded>
    </item>
    <item>
      <title>Протоколы связи микроконтроллеров: проектирование и парсинг</title>
      <link>https://tproger.ru/articles/protokoly-svyazi-mikrokontrollerov-proektirovanie-i-parsing</link>
      <comments>https://tproger.ru/articles/protokoly-svyazi-mikrokontrollerov-proektirovanie-i-parsing?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/protokoly-svyazi-mikrokontrollerov-proektirovanie-i-parsing</guid>
      <description><![CDATA[<p>Разбираем структуру кадра, контрольные суммы и конечные автоматы для обмена данными между хостом и микроконтроллером. Примеры на C — читайте и внедряйте.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/protokoly-svyazi-mikrokontrollerov-proektirovanie-i-parsing">Протоколы связи микроконтроллеров: проектирование и парсинг</a>»</p>]]></description>
      <category><![CDATA[Алгоритмы и структуры данных]]></category>
      <category><![CDATA[Низкоуровневое программирование]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Hardware]]></category>
      <category><![CDATA[Сетевые протоколы]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 09 Jun 2026 07:25:27 GMT</pubDate>
      <content:encoded><![CDATA[<p>Протокол связи микроконтроллера — это набор правил формирования кадров поверх физического интерфейса (UART, RS-485). Если вы когда-либо писали прошивку, которой нужно «разговаривать» с компьютером, то знаете: надёжность связи важнее скорости. Один потерянный байт или сбой в синхронизации — и устройство зависает или выполняет чужую команду. В этой статье разберём, как проектировать собственные протоколы связи микроконтроллеров: от структуры кадра до парсера на конечном автомате.</p><h2>Что такое протокол передачи данных</h2><p>Под <b>протоколом передачи данных</b> здесь понимается формат пакетов (кадров), которые строятся поверх физического уровня. Физический уровень — это уже выбранный интерфейс: RS-232, RS-485, инфракрасный канал, радиомодуль или оптоволокно. От него мы получаем две базовые операции: отправить один байт и принять один байт. Всё остальное — наша задача.</p><p>Данные передаются пакетами — <b>кадрами</b>. Хороший кадр позволяет приёмнику понять: где начало сообщения, кому оно адресовано, сколько в нём полезных данных и не повредились ли они по дороге.</p><p>Кадр состоит из заголовка, адресов, типа данных, длины, полезной нагрузки, контрольной суммы и концевика.</p><p>Для защиты от случайных совпадений заголовок делают многобайтовым, а контрольную сумму считают по всей значащей части кадра.</p><p>Парсинг удобно реализовывать конечным автоматом: каждый принятый байт переводит машину в новое состояние.</p><p>На хосте данные почти всегда буферизуются; на простых МК часто выгоднее передавать напрямую, чтобы сэкономить ОЗУ.</p><h2>Структура кадра: из чего собирать пакет</h2><p>Надёжный протокол обычно включает семь логических полей. Не все обязательны — выбор зависит от задачи.</p><h3>Заголовок и концевик</h3><p><b>Заголовок</b> (frame header) и <b>концевик</b> (frame tail) отмечают границы кадра. Главное требование — минимизировать вероятность случайного совпадения этих байтов в потоке данных. Есть два подхода:</p><ul><li><b>Подбор характерных байтов.</b> Если данные предсказуемы (например, только ASCII-текст), можно выбрать заголовок из диапазона непечатаемых символов.</li><li><b>Увеличение длины.</b> Для случайных данных лучше сделать заголовок многобайтовым — например, 0x55 0xAA 0x7E. Вероятность случайного совпадения трёх конкретных байтов подряд падает экспоненциально. Даже если совпадение произойдёт, его отловит контрольная сумма.</li></ul><h3>Адресация</h3><p><b>Адрес назначения</b> нужен в системах типа «один к многим» — например, когда один хост управляет несколькими датчиками по общей шине RS-485. В сложных сетях добавляют ещё и <b>адрес источника</b>, чтобы получатель знал, от кого пришёл пакет.</p><h3>Тип, длина и данные</h3><p><b>Тип данных</b> (data type) говорит, что дальше идёт команда или полезная нагрузка. <b>Длина</b> (data length) указывает число значащих байтов в блоке данных. Вместе они образуют «тело» кадра — ту часть, которую мы действительно хотим доставить.</p><h3>Контрольная сумма</h3><p><b>Контрольная сумма</b> проверяет целостность кадра. Простейший вариант — арифметическая сумма всех байтов тела. Для более серьёзной защиты применяют CRC (циклический избыточный код): он ловит пакетные ошибки и перестановки битов, которые простая сумма пропустит. Выбор алгоритма — компромисс между скоростью вычисления на МК и требуемой надёжностью.</p><h2>Передача: хост и микроконтроллер</h2><p>На физическом уровне отправка сводится к посылке байтов один за другим. Но способ организации этой посылки сильно влияет на производительность.</p><h3>Передача с микроконтроллера</h3><p>На простых контроллерах вроде семейства 8051 часто используют <b>прямую передачу</b>: процессор загружает байт в буфер UART и ждёт флага готовности. Плюс — данные моментально оказываются на линии. Минус — процессор занят на всё время отправки.</p><p>Альтернатива — <b>передача по прерыванию</b>: байты складываются в кольцевой буфер, а прерывание UART отправляет их фоном. Экономит процессорное время, но требует ОЗУ под буфер. На 8051 с его скудной памятью прямая передача часто выигрывает.</p><h3>Передача с хоста</h3><p>На ПК данные почти всегда буферизуются операционной системой. Программист работает с тремя уровнями абстракции:</p><ol><li><b>Контролы ОС.</b> В Windows — компоненты вроде MSComm (устаревший 32-bit ActiveX, не рекомендуется для новых проектов). Просто, но нужно следить за блокировками при приёме и многопоточностью.</li><li><b>Системные API.</b> В Windows и Linux последовательный порт — это файл. Открываем через CreateFile или open(), но перед чтением и записью требуется настройка параметров порта (в Linux — termios со скоростью, чётностью и размером слова).</li><li><b>Класс-обёртка.</b> Например, CSerialPort для Windows: он инкапсулирует инициализацию, поток приёма и отправку. После открытия порта вызов WriteToPort отправляет массив байтов, а внутренний поток следит за входящими данными и шлёт сообщения родительскому окну.</li></ol><h2>Приём и парсинг на конечном автомате</h2><p>Приём данных на стороне микроконтроллера тоже бывает двух видов: <b>опрос</b> (поллинг) и <b>прерывание</b>. Опрос проще, но отнимает процессорное время. Прерывание эффективнее: байт пришёл — сработала процедура обработки прерывания (ISR, Interrupt Service Routine), процессор отвлёкся на доли миллисекунды и вернулся к задаче.</p><p>Где размещать парсер протокола? Если протокол простой, его можно держать прямо в обработчике прерывания: приняли корректный кадр — установили флаг, основной цикл реагирует. Для сложных протоколов лучше писать байты в буфер и разбирать их в главном цикле. Гибридный подход тоже возможен: в прерывании ищем только «команду подключения», а остальное парсим фоном.</p><h3>Пример: разбор кадра по состояниям</h3><p>Рассмотрим конкретный формат кадра:</p><p>Парсер реализуем через переменную state_machine. Каждое состояние соответствует ожидаемому байту. Если пришёл не тот байт — автомат сбрасывается в ноль. Это защищает от «залипания» в промежуточном состоянии при обрыве связи или помехах.</p><p><b>Совет по стабильности:</b><br />Сброс автомата при несовпадении заголовка, адресов, длины, checksum или концевика — ключевая техника. Без неё при обрыве кадра парсер застрянет в промежуточном состоянии, и следующие кадры будут отвергнуты.</p><h2>Приём на стороне хоста</h2><p>На ПК приём организуется проще: операционная система уже буферизует входящие байты. Для неблокирующего чтения запускают отдельный поток, который ждёт данных из порта и передаёт их в основной процесс через сообщения или обратный вызов. Класс CSerialPort, например, отправляет родительскому окну сообщение WM_COMM_RXCHAR с каждым новым байтом. Обработчик этого сообщения просто вызывает тот же парсер, что и на микроконтроллере.</p><p>Таким образом, логика разбора протокола <b>единая</b> для обеих сторон. Различается только способ доставки байтов в парсер: прерывание ISR на МК и поток ОС на хосте.</p><h2>Выводы</h2><p>Проектирование протокола связи для микроконтроллера — это не ракетостроение, но требует дисциплины. Хороший кадр защищает границы (заголовок + концевик), адресует получателя, указывает длину и проверяет целостность. Парсинг на конечном автомате делает код предсказуемым и устойчивым к сбоям.</p><p>Главный принцип — <b>сбрасывать автомат при любом нарушении ожидаемого шаблона</b>. Это предотвращает «залипание» и позволяет системе быстро восстановиться после помехи. На основе этой базы можно наращивать надёжность: добавлять повторные передачи, порядковые номера, шифрование — в зависимости от требований проекта.</p><blockquote>Хороший протокол — это не тот, который передаёт быстрее всех, а тот, который не ломается при помехах.</blockquote><p><b>Источник:</b> оригинальная статья Leo Liu — <a href="https://dev.to/sienovoleo/microcontroller-communication-protocol-design-1e29">Microcontroller Communication Protocol Design</a> (DEV Community).</p>]]></content:encoded>
    </item>
    <item>
      <title>Семь ошибок индексации БД, которые убивают производительность SaaS на корню</title>
      <link>https://tproger.ru/articles/sem-owibok-indeksacii-bd-kotorye-ubivayut-proizvoditelnost-sa</link>
      <comments>https://tproger.ru/articles/sem-owibok-indeksacii-bd-kotorye-ubivayut-proizvoditelnost-sa?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/sem-owibok-indeksacii-bd-kotorye-ubivayut-proizvoditelnost-sa</guid>
      <description><![CDATA[<p>Разбираем 7 типичных ошибок индексации в PostgreSQL и MySQL: переиндексация, низкая селективность, раздутие индексов, мультитенантность и внешние ключи. Проверьте свою БД перед релизом.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/sem-owibok-indeksacii-bd-kotorye-ubivayut-proizvoditelnost-sa">Семь ошибок индексации БД, которые убивают производительность SaaS на корню</a>»</p>]]></description>
      <category><![CDATA[Алгоритмы и структуры данных]]></category>
      <category><![CDATA[Масштабируемость и ограничения памяти]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 03 Jun 2026 05:30:09 GMT</pubDate>
      <content:encoded><![CDATA[<p>Ваш код чист, архитектура продумана, а запросы на staging укладываются в 12 мс. Но при 500 000 записей всё встаёт: панели мониторинга тормозят, пользователи жалуются, а дежурный инженер в полночь разглядывает план выполнения запроса и не понимает, что пошло не так. В девяти случаях из десяти причина — не отсутствие индексов, а <b>неправильные индексы</b>.</p><p><b>Индекс в базе данных</b> — это вспомогательная структура (чаще всего B-дерево; хеш-индексы, GiST, GIN и другие типы применяются в узких специфических сценариях), которая ускоряет выборку строк по заданным колонкам. По аналогии с оглавлением книги: вместо перелистывания всех страниц СУБД сразу переходит к нужной главе. Но если оглавление построено плохо, пользы от него нет — а вот накладные расходы на поддержку остаются.</p><p>В этой статье разберём семь самых разрушительных ошибок индексации в production-SaaS: от параноидального создания индексов «на всякий случай» до игнорирования раздутия и особенностей мультитенантных схем. Каждая ошибка — с примерами SQL, метриками и конкретным фиксом.</p><p>Индексы — это налог на запись: каждый INSERT, UPDATE и DELETE обновляет все индексы таблицы.</p><p>Индекс на колонке с низкой селективностью (boolean, статус) почти бесполезен — используйте частичные (partial) индексы.</p><p>В составном индексе порядок колонок критичен: PostgreSQL может использовать (a, b, c) только если условие начинается с a.</p><p>Раздутые индексы на высокоизменяемых таблицах могут занимать в 8 раз больше места, чем нужно — планируйте REINDEX CONCURRENTLY.</p><p>В мультитенантных системах индекс по tenant_id один часто недостаточен: добавляйте диапазон времени или тип события.</p><p>Внешние ключи в PostgreSQL не индексируются автоматически — после каждого FOREIGN KEY создавайте индекс вручную.</p><p>Без EXPLAIN ANALYZE индексы создаются вслепую. Проверяйте реальный план выполнения перед деплоем.</p><h2>Ошибка 1. Индексировать всё подряд «на всякий случай»</h2><p>Самая распространённая ошибка — не недостаток индексов, а их избыток из тревожности. Особенно часто в неё попадают junior-разработчики: добавляют индекс на каждую колонку, которая встречается в WHERE, «на всякий случай». Кажется ответственным, но на самом деле вредно.</p><p>Каждый индекс — это налог на запись. При INSERT, UPDATE и DELETE PostgreSQL (или MySQL) вынуждена обновлять <b>все</b> индексы таблицы. На таблице с 8 индексами каждая запись трогает 8 структур данных. При низкой нагрузке это невидимо, но при 10 000 записей в минуту это становится узким местом.</p><p><b>Аудит неиспользуемых индексов в PostgreSQL:</b><br />Запрос к pg_stat_user_indexes покажет, сколько раз каждый индекс применялся с момента сброса статистики. Если idx_scan = 0 — индекс кандидат на удаление.</p><p>Не удаляйте индекс сразу — сначала убедитесь, что он не нужен для редких, но критичных отчётов. Но если он месяцами не используется, смело избавляйтесь.</p><h2>Ошибка 2. Не понимать селективность</h2><p>Индекс на булеву колонку — почти всегда бесполезен. <b>Селективность</b> измеряет, сколько различных значений содержится относительно общего числа строк. У boolean всего два значения. Если 95% строк имеют is_active = true, планировщик запросов проигнорирует индекс и сделает Seq Scan — и будет прав.</p><p>Правило большого пальца: если у колонки меньше 10–20 уникальных значений относительно размера таблицы, простой индекс по ней один не справится. Используйте частичные или составные индексы.</p><h2>Ошибка 3. Неверный порядок колонок в составном индексе</h2><p>Составные индексы мощны, но часто неправильно понимаются. PostgreSQL может использовать индекс (a, b, c) для фильтрации по a, a и b, a, b и c. Но <b>не может</b> эффективно использовать его, если запрос фильтрует только по b или c — ведущая колонка пропущена.</p><p>Решение: первыми ставьте колонки с equality-условиями, затем — по селективности, и проектируйте индексы вокруг <b>реальных паттернов запросов</b>, а не вокруг схемы таблицы. Также учитывайте ORDER BY и необходимость покрывающего индекса. Перед созданием обязательно запускайте EXPLAIN ANALYZE.</p><h2>Ошибка 4. Игнорировать раздутие индексов</h2><p>Индексы деградируют со временем. Многие инженеры воспринимают их как «поставил и забыл», но это заблуждение. В PostgreSQL при обновлении или удалении строки старые записи в индексе устаревают и накапливаются как <b>раздутие</b> (bloat); их физическое удаление происходит при выполнении VACUUM. На высокоизменяемых таблицах (заказы, события, логи, сессии) раздутие накапливается стремительно.</p><p>Таблица с 1 млн живых строк может иметь индекс, раздутый до размеров 8 млн записей. Каждый запрос через такой индекс делает в 8 раз больше работы, чем должен.</p><p><b>REINDEX CONCURRENTLY</b> — ключевое слово. Обычный REINDEX блокирует таблицу, а в production-SaaS это прямой путь к инциденту. CONCURRENTLY перестраивает индекс без блокировок, хотя и медленнее.</p><p>Также убедитесь, что autovacuum настроен под вашу реальную нагрузку на запись. Значения по умолчанию в PostgreSQL консервативны и часто недостаточны для SaaS с высокой интенсивностью записи.</p><h2>Ошибка 5. Индексы на колонках с малым числом уникальных значений в мультитенантных системах</h2><p>В мультитенантной архитектуре почти каждый запрос фильтрует по tenant_id. Естественное желание — проиндексировать эту колонку. Но для крупных тенантов индекс по tenant_id вернёт слишком много строк, и планировщик предпочтёт Seq Scan. Для маленьких тенантов отдельный индекс по tenant_id может быть полезен.</p><p>На серьёзном масштабе правильное решение — партиционирование таблиц по tenant_id, но это архитектурное решение. Практический первый шаг — составные индексы с временными диапазонами.</p><h2>Ошибка 6. Не индексировать внешние ключи</h2><p>В PostgreSQL внешние ключи <b>не индексируются автоматически</b>. При удалении родительской строки СУБД должна проверить все дочерние таблицы на наличие ссылающихся записей — и без индекса на внешнем ключе это Seq Scan по каждой дочерней таблице. На таблице orders с 50 млн строк удаление пользователя вызывает полное сканирование.</p><p>Сделайте это командным соглашением: в чек-листе миграций обязательный пункт «после каждого FOREIGN KEY — CREATE INDEX».</p><h2>Ошибка 7. Не использовать EXPLAIN ANALYZE перед деплоем</h2><p>Большинство решений об индексах принимаются интуитивно. Интуиция ошибается достаточно часто, чтобы это стало проблемой. EXPLAIN ANALYZE показывает, что именно делает планировщик: какие индексы использует, какие игнорирует, сколько строк реально прочитал против оценки, где тратится время.</p><p>На что обращать внимание:<br />• <b>Seq Scan</b> на большой таблице при выборке малой доли строк — возможно, пропущен индекс или планировщик не может использовать существующий индекс.<br />• <b>Rows Removed by Filter</b> в десятки тысяч — индекс есть, но неправильные колонки или низкая селективность.<br />• <b>Buffers: shared hit=0 read=45000</b> — данные не закэшированы (cold cache), страницы читаются с диска. Для диагностики раздутия смотрите общее число буферов и сравнивайте размер индекса с ожидаемым.<br />• Высокое <b>actual time</b> — проверьте раздутие, актуальность статистики, количество буферов, дисковую подсистему и наличие блокировок. Запустите ANALYZE tablename, чтобы обновить статистику планировщика.</p><h2>Чек-лист индексации для SaaS</h2><ul><li>У каждой колонки внешнего ключа есть индекс?</li><li>Составные индексы упорядочены по селективности, а не по удобству?</li><li>Булевы и низкокардинальные фильтры используют частичные индексы вместо полных?</li><li>Вы запускали EXPLAIN ANALYZE на 10 самых медленных запросов за неделю?</li><li>Есть процесс поиска и удаления неиспользуемых индексов?</li><li>Высокоизменяемые таблицы регулярно проходят REINDEX CONCURRENTLY?</li><li>Autovacuum настроен под реальный объём записи, а не под значения по умолчанию PostgreSQL?</li><li>В мультитенантных таблицах индексы начинаются с tenant_id и включают диапазон времени?</li></ul><h2>Выводы</h2><p>Индексы — не фича производительности, которую добавляют, когда всё начинает тормозить. Это проектное решение, которое принимается вместе со схемой, и пересматривается по мере эволюции паттернов запросов. Команды, которые уверенно масштабируются, — не те, у кого больше всего индексов, а те, кто понимает, что каждый индекс стоит, что даёт и когда его пора убирать.</p><blockquote>База данных, которая быстра на 10 000 строках и быстра на 50 миллионах, — не случайность. Это результат того, что кто-то считал планирование запросов первоклассной инженерной задачей, а не рутинным дополнением.</blockquote><p>Проверьте свои самые медленные запросы этой недели — возможно, одна из этих семи ошибок уже сидит в вашей production-базе.</p><p><b>Источник и материалы:</b><br />• <a href="https://dev.to/outworktech/database-indexing-mistakes-that-kill-saas-performance-at-scale-4j8e">Database Indexing Mistakes That Kill SaaS Performance at Scale</a> — оригинальная статья OutworkTech<br />• <a href="https://www.postgresql.org/docs/current/indexes.html">PostgreSQL Index Types</a> — официальная документация<br />• <a href="https://www.postgresql.org/docs/current/sql-reindex.html">REINDEX</a> — документация по перестроению индексов</p>]]></content:encoded>
    </item>
    <item>
      <title>MDLM против LLM: диффузионные world models для RL</title>
      <link>https://tproger.ru/articles/maskirovannye-diffuzionnye-modeli-obhodyat-llm-v-4-raza-bolwego</link>
      <comments>https://tproger.ru/articles/maskirovannye-diffuzionnye-modeli-obhodyat-llm-v-4-raza-bolwego?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/maskirovannye-diffuzionnye-modeli-obhodyat-llm-v-4-raza-bolwego</guid>
      <description><![CDATA[<p>Исследование Patronus AI: маскированные диффузионные модели превосходят авторегрессионные LLM в симуляции сред. 8B обходит 35B, GRPO даёт +47%. Проверьте выводы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/maskirovannye-diffuzionnye-modeli-obhodyat-llm-v-4-raza-bolwego">MDLM против LLM: диффузионные world models для RL</a>»</p>]]></description>
      <category><![CDATA[Алгоритмы и структуры данных]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Инновации]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 28 May 2026 13:42:38 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы тренируете LLM-агентов для работы с инструментами, скорее всего, вы сталкиваетесь с одной и той же проблемой: среды для обучения быстро становятся слишком простыми, и агент переобучается на узкий набор сценариев. Исследователи из Patronus AI предложили необычное решение — вместо авторегрессионных языковых моделей использовать <b>маскированные диффузионные языковые модели (MDLM)</b> в качестве «воображаемых» сред. Результат: 8-миллиардная модель обыгрывает 35-миллиардную автрегрессионную LLM, а обучение агентов на синтетических траекториях даёт прирост точности до 47%.</p><p>Разбираем, почему бидирекционный денойзинг оказался эффективнее привычного лево-направо декодирования, и что это значит для разработчиков интеллектуальных агентов.</p><p>Маскированные диффузионные языковые модели (MDLM) превосходят авторегрессионные LLM как модели мира (world models) для текстовых сред благодаря бидирекционному денойзингу и учёту якорей (anchors).</p><p>8-миллиардная SDAR обходит 35-миллиардную Qwen-3.5-35B-A3B по метрике MAUVE (0,982 против 0,932) и порождает состояния большего разнообразия.</p><p>GRPO-обучение на траекториях от MDLM даёт прирост до +47% на отложенных (held-out) средах (ALFWorld, ScienceWorld, AppWorld).</p><p>Экспертная оценка подтверждает высокий реализм (4,75/5) и корректность (4,25/5) сгенерированных состояний.</p><p>Ограничения: хрупкая генерация API-ключей, pagination drift и блоковые повторы при высокой temperature.</p><p>В контексте обучения с подкреплением (RL) <b>world model</b> — это генеративная модель, которая предсказывает следующее состояние среды на основе текущего состояния и действия агента. Вместо того чтобы взаимодействовать с реальным API или базой данных на каждом шаге обучения, агент может «играть» в симуляции, созданной world model. Это дешевле, быстрее и позволяет порождать редкие или опасные сценарии, которые сложно воспроизвести в реальной системе.</p><p>Ранее для текстовых сред чаще всего использовали обычные автрегрессионные LLM: подали префикс — модель дописала продолжение. Но у этого подхода есть фундаментальное ограничение: состояния среды часто содержат <b>якоря (anchors)</b> — фиксированные поля, которые должны оставаться неизменными (например, user_id или status: "error"). Авторегрессионная модель, генерирующая текст строго слева направо, не видит эти якоря до тех пор, пока не дойдёт до них, и рискует сгенерировать противоречивое состояние.</p><h2>Почему автрегрессионные LLM плохо справляются</h2><p>Представьте, что модели нужно сгенерировать ответ API о возврате платежа. В схеме зафиксированы поля ticket_id и user_id, а также конечный статус "error". Авторегрессионная модель начинает декодирование с первого поля и не видит финальный статус до самого конца. В результате она может сгенерировать refund_processed: true, хотя статус требует ошибки — получается глобально некорректное состояние.</p><p>Этот эффект называется <b>left-to-right bias</b>: causal attention маскирует будущие токены, и модель вынуждена принимать решения без учёта глобальных ограничений. При масштабировании длины траекторий ошибки накапливаются, что приводит к mode collapse — агент видит всё меньше разнообразных ситуаций и переобучается.</p><p><b>Проблема в цифрах:</b><br />Исследователи показали, что даже 35-миллиардная авторегрессионная модель (Qwen-3.5-35B-A3B) уступает 8-миллиардной диффузионной по метрике MAUVE, измеряющей согласованность распределений сгенерированных и реальных состояний.</p><h2>Как работают маскированные диффузионные модели</h2><p>Маскированные диффузионные языковые модели (MDLM) обучаются итеративно восстанавливать замаскированные токены в последовательности. В отличие от авторегрессионных моделей, они используют <b>бидирекциональное внимание</b>: при подавлении шума каждой позиции модель видит все уже известные и все ещё замаскированные токены одновременно. Это позволяет учитывать якоря с обеих сторон последовательности.</p><p>Ключевое преимущество для world modeling — <b>any-order generation</b>. MDLM может заполнять поля состояния в произвольном порядке: сначала зафиксировать статус ошибки, потом подобрать под него корректный код ошибки, а уже затем заполнить остальные поля. Такой подход естественным образом поддерживает глобальную согласованность состояния и снижает накопление ошибок.</p><p>Ещё одно отличие — стохастичность самого порядка генерации. В авторегрессионной модели temperature влияет только на выбор токена; в MDLM она влияет ещё и на то, <b>какую позицию</b> раскрывать следующей. Это даёт дополнительную ось разнообразия и помогает избежать коллапса моды (mode collapse) на высоковероятных префиксах.</p><h2>Результаты: 8B против 35B и zero-shot transfer</h2><p>Авторы исследования собрали крупный датасет из сотен тысяч траекторий из девяти открытых сред (SWE-bench, CoderForge, TauBench, Gorilla, Toolathlon и других), сгенерированных передовыми моделями. На этих данных они дообучили несколько MDLM (SDAR-8B, SDAR-30B-A3B, WeDLM-8B, LLaDA-2.1-mini) и сравнили с авторегрессионными базовыми линиями (Qwen-3.5-27B, GPT-OSS-20B, GLM-4.7-Flash, Nemotron-3-Nano-30B, Qwen-3.5-35B-A3B).</p><h3>Генерация состояний</h3><p>На in-domain тесте SDAR-8B достиг MAUVE = 0,982, тогда как сильнейшая AR-модель (Qwen-3.5-35B-A3B) — всего 0,932. На out-of-domain наборах (API-Bank, OccuBench, Intercode-SQL) разрыв сохраняется: SDAR-8B показывает MAUVE 0,979 против 0,960 у 35-миллиардного конкурента. При этом Self-BLEU у SDAR-8B ниже (0,601 против 0,690), а Distinct-N выше (0,385 против 0,253) — это означает, что диффузионная модель порождает состояния большего разнообразия.</p><h3>Обучение агентов без дообучения среды</h3><p>Главный практический тест — можно ли использовать траектории от MDLM для обучения агентов в совершенно новых средах? Авторы применили GRPO (Group Relative Policy Optimization) с траекториями от SDAR-8B и Qwen-3.5-27B на трёх отложенных (held-out) средах: AppWorld, ScienceWorld и ALFWorld. Результаты впечатляют:</p><ul><li>LFM2.5-1.2B на ALFWorld: с 5,7% (база) до 53,6% с SDAR-WM — прирост +47,9% абсолютных пунктов.</li><li>Mistral-7B на ScienceWorld: с 3,3% до 48,4% — прирост +45,1%.</li><li>Qwen3-4B на AppWorld: с 33,3% до 62,0% — прирост +28,7%.</li><li>Во всех девяти пара модель-среда SDAR-WM превосходит Qwen-WM в среднем на +5,3 пункта.</li></ul><p>Критически важно: это <b>zero-shot transfer</b> — агенты не видели целевые среды во время обучения world model. Ранее подобные результаты требовали специфической адаптации под каждую среду.</p><h3>Оценка людьми</h3><p>Четыре независимых эксперта с опытом работы с LLM-агентами оценили 100 сгенерированных состояний по шкале Лайкера 1–5. Средние оценки SDAR: <b>4,75</b> (реализм), <b>4,25</b> (корректность исхода), <b>4,50</b> (польза для обучения). Коэффициент согласия Криппендорфа α ≥ 0,89 на всех метриках говорит о высокой межаннотаторной надёжности.</p><h2>Ограничения и подводные камни</h2><p>Несмотря на впечатляющие цифры, у MDLM как world models есть свои слабые места. Во-первых, модели испытывают трудности с генерацией структурированных полей вроде API-ключей: комбинация safety-alignment базовой Qwen и малого block size при диффузии приводит к искажённым строкам. Во-вторых, при работе с постраничным выводом (pagination) MDLM иногда теряет нумерацию страниц — эффект, который авторы назвали <b>pagination drift</b>.</p><p>Также при высоких температурах модель может зацикливаться на блоковом уровне, повторяя одни и те же фрагменты состояния. Авторы отмечают, что все три ограничения, скорее всего, ослабнут с ростом размера модели и специализированным дообучением на задачи работы с инструментами.</p><h2>Что это значит для разработчиков</h2><p>Для инженеров, строящих агентных систем, исследование открывает сразу несколько перспектив:</p><ol><li>Синтетические данные для RL. Если у вас нет доступа к реальному контуру заказчика, MDLM-модель может сгенерировать вполне реалистичные траектории для предварительного обучения агента, прежде чем вы перейдёте к дорогим реальным вызовам.</li><li>Аугментация редких сценариев. Диффузионная природа MDLM даёт естественный способ порождать редкие ошибки и пограничные случаи, которые плохо представлены в логах production.</li><li>Эффективность по параметрам. 8-миллиардная диффузионная модель может заменить 30-миллиардную автрегрессионную в задаче симуляции — это снижает требования к инфраструктуре вывода.</li><li>Управляемость. Возможность задавать якоря и направлять генерацию через маскирование упрощает создание отобранных обучающих выборок под конкретные домены.</li></ol><p>Если тема RL и агентов вам близка, загляните в наши материалы: <a href="https://tproger.ru/articles/kak-rabotaet-reinforcement-learning">как работает Reinforcement Learning</a>, <a href="https://tproger.ru/news/v-llama-cpp-smerzhili-mtp-dekoding-qwen3-6-27b-stal-v-2-4-raza">ускорение Qwen3.6 в llama.cpp</a> и <a href="https://tproger.ru/articles/personalnyj-ii--zapuskaem-llama-i-mistral-lokalno-na-mac-i-windows">локальный запуск Mistral</a>.</p><p>Важно понимать, что MDLM не заменяют полностью реальные среды: они скорее служат мостом между supervised fine-tuning и дорогим RL в production, позволяя дешево итерировать политику агента.</p><h2>Выводы</h2><p>Исследование Patronus AI демонстрирует, что архитектурный выбор важнее сырого количества параметров: 8-миллиардная маскированная диффузионная модель превосходит 35-миллиардную авторегрессионную в задаче world modeling благодаря бидирекционному денойзингу и способности учитывать якоря состояния. Для разработчиков агентных систем это открывает путь к дешёвым и разнообразным синтетическим средам для обучения.</p><blockquote>Главный вывод исследования: архитектура важнее размера. 8-миллиардная диффузионная модель обходит 35-миллиардную авторегрессионную, потому что видит всё состояние целиком, а не только префикс.</blockquote><p>Если вы планируете внедрять RL в агентные пайплайны, стоит следить за развитием MDLM: с ростом масштаба и специализации эта архитектура может стать стандартом де-факто для симуляции текстовых сред.</p><p><b>Источник:</b> <a href="https://zenodo.org/records/20219105">Deshpande D. Masked Diffusion Language Models are Strong and Steerable Text-Based World Models for Agentic RL. Zenodo, 2026. DOI: 10.5281/zenodo.20219105</a>. <i>Исследование пока доступно только в виде препринта, его выводы стоит воспринимать с осторожностью.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Шесть способов сделать flatten в Python — какой быстрее в 500 раз</title>
      <link>https://tproger.ru/translations/west-sposobov-sdelat-flatten-v-python-kakoj-bystree-v-500-ra</link>
      <comments>https://tproger.ru/translations/west-sposobov-sdelat-flatten-v-python-kakoj-bystree-v-500-ra?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/west-sposobov-sdelat-flatten-v-python-kakoj-bystree-v-500-ra</guid>
      <description><![CDATA[<p>Разворачиваем вложенный список в Python: for + extend, list comprehension, itertools.chain, reduce, sum, NumPy. Замер и почему sum() — анти-паттерн.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/west-sposobov-sdelat-flatten-v-python-kakoj-bystree-v-500-ra">Шесть способов сделать flatten в Python — какой быстрее в 500 раз</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 12 May 2026 11:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если в коде встречается список списков и нужно превратить его в один плоский, на ум приходит как минимум пять подходов — от обычного for до itertools.chain и NumPy. Какие из них работают за миллисекунды, а какие — за секунды? Перевод и адаптация туториала Real Python с разбором всех способов и замером производительности.</p><p>Flatten — операция приведения вложенной структуры к одномерной. Самый распространённый кейс: матрица представлена как список списков, а вам нужен плоский список всех значений, чтобы передать в алгоритм или ML-модель. В отличие от других языков, в Python нет встроенного .flatten() у списков — поэтому стандартное «как это сделать?» имеет несколько ответов разной степени читаемости и эффективности.</p><p>Пусть у нас есть матрица 4×4:</p><p>Цель — получить:</p><ul><li>Быстрее всего работает обычный цикл с in-place мутацией: += или .extend(). Время — около 5–7 мс.</li><li>itertools.chain() — около 16 мс, но в 3 раза экономнее по памяти; подходит, когда финальный список не нужен полностью.</li><li>List comprehension — около 24 мс. Читаемо и без побочных эффектов, но создаёт промежуточные объекты.</li><li>functools.reduce() + lambda и sum() — от 2,6 секунд. Это в 400–500 раз медленнее за счёт постоянного создания новых промежуточных списков.</li><li>Для произвольной вложенности — рекурсия или итеративный обход со стеком.</li><li>В data-science для NumPy-массивов используйте np.ndarray.flatten() — он быстрее любого варианта на чистых списках.</li></ul><h2>Способ 1. Цикл for + .extend()</h2><p>Самый прямолинейный и читаемый вариант: проходим по каждой подсписку и добавляем её элементы к итоговому списку через .extend().</p><p>.extend() принимает любой iterable и поэлементно добавляет его в конец списка. Альтернативно можно использовать augmented-concatenation +=:</p><p>Обе функции делают одно и то же. Real Python отмечает: flatten_extend читается чуть лучше, потому что метод named — намекает на смысл операции. По скорости варианты идут в первой тройке.</p><h2>Способ 2. List comprehension</h2><p>Pythonic-альтернатива однострочнику. Двойной for внутри comprehension перебирает сначала строки, потом элементы строк:</p><p>Подход компактнее цикла и не плодит промежуточных переменных. Скорость — заметно медленнее .extend() из-за двух вложенных циклов на уровне comprehension, но всё ещё в диапазоне десятков миллисекунд.</p><h2>Способ 3. itertools.chain() — экономно по памяти</h2><p>chain() возвращает итератор, а не список. Это значит, что вы можете обходить элементы потокового, не материализуя весь массив в памяти — полезно, когда матрица большая и хранить полный flat-список нерационально.</p><p>На большой матрице chain.from_iterable примерно в три раза медленнее цикла .extend(): накладные расходы на материализацию через list(). Но если итоговый список как таковой не нужен — можно отдать сам итератор потребителю и сэкономить и время, и память.</p><h2>Способ 4. functools.reduce() и sum() — почему так медленно</h2><p>Эти варианты часто появляются в туториалах для красоты, но на практике плохо масштабируются. Причина одна: каждая итерация создаёт новый промежуточный список вместо мутации существующего.</p><p>iconcat мутирует аккумулятор и держится в топ-3 по скорости. А вот вариант с lambda и оператором + на каждом шаге копирует обе стороны — отсюда и взрывной рост времени на больших данных.</p><p>С sum() ситуация ровно такая же:</p><p>sum() работает на любых сложениях, но для списков это О(n²) по аллокациям. На матрице 1000×1000 он в ~500 раз медленнее цикла с .extend().</p><h2>Способ 5. Произвольная вложенность — рекурсия и стек</h2><p>Все предыдущие способы предполагают, что вложенность ровно одна. Если структура произвольно глубокая или неоднородная, нужен другой подход:</p><p>Рекурсия удобна, пока глубина не упирается в sys.getrecursionlimit() (по умолчанию 1000). Для очень глубоких структур стоит переписать через итеративный обход со стеком:</p><h2>Способ 6. NumPy для data-science</h2><p>Если вы работаете с матрицами в data-science, у numpy.ndarray есть готовый метод .flatten(). Он быстрее любого варианта на чистых Python-списках, потому что под капотом работает с непрерывным блоком памяти.</p><h2>Замер производительности</h2><p>Real Python прогнали все варианты на матрице 1000×1000 через timeit. Результаты, отсортированные по времени:</p><p>Между первыми тремя и последними четырьмя — пропасть в 400–500 раз. Причина: топовые функции мутируют существующий список, а reduce(+), reduce(operator.add), reduce(lambda x, y: x + y) и sum() на каждой итерации создают копии.</p><h2>FAQ</h2><h2>Выводы</h2><p>Если вам нужен один-в-один поведенческий рецепт: for row in matrix: flat.extend(row). Этот код понятен любому, кто знает Python, и упирается в производительность только когда матрица переваливает за миллионы элементов. Если объёмы такие, что нужна потоковая обработка — переключитесь на itertools.chain. Если данные numerical — используйте NumPy.</p><p>Главное, чего стоит избегать — sum(matrix, []) и reduce с + или lambda. Они смотрятся изящно в туториалах, но превращают O(n) в O(n²) и ставят вас в первую же боттлнек-историю на проде.</p><p>Оригинальный туториал с пошаговыми объяснениями и сравнением — <a href="https://realpython.com/python-flatten-list/">на Real Python</a>, автор — Leodanis Pozo Ramos.</p>]]></content:encoded>
    </item>
    <item>
      <title>Async Rust до сих пор в MVP: четыре оптимизации компилятора</title>
      <link>https://tproger.ru/translations/async-rust-do-sih-por-v-mvp-chetyre-optimizacii-kompilyatora</link>
      <comments>https://tproger.ru/translations/async-rust-do-sih-por-v-mvp-chetyre-optimizacii-kompilyatora?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/async-rust-do-sih-por-v-mvp-chetyre-optimizacii-kompilyatora</guid>
      <description><![CDATA[<p>Tweede golf разобрал, откуда bloat в async Rust, и предложил Project Goal на €30 000. 2–5% размера прошивки только за замену panic на Pending в Returned-состоянии.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/async-rust-do-sih-por-v-mvp-chetyre-optimizacii-kompilyatora">Async Rust до сих пор в MVP: четыре оптимизации компилятора</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 06 May 2026 13:15:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Async Rust продавали как «zero cost abstraction» — обещание, что асинхронный код по размеру и скорости должен быть равен ручному state machine на синхронном Rust. На бумаге это даёт executor-agnostic код (один и тот же исходник запускается под Tokio на сервере и под Embassy на микроконтроллере), на практике — на embedded особенно — видно: каждый байт прошивки на счету, а async добавляет десятки и сотни лишних строк сгенерированного кода. Команда из голландской Rust-консалтинговой компании Tweede golf разобрала, откуда берётся этот bloat и как его срезать на уровне компилятора — с конкретными цифрами и Rust Project Goal.</p><p>Это вторая часть серии. В первой — обходные приёмы для async-кода прямо в пользовательском коде. Во второй — автор копает в MIR (Mid-level Intermediate Representation — внутреннее представление rustc между исходником и LLVM IR) и формулирует, что должен научиться делать сам компилятор. Цель — не лечить симптомы, а добраться до корня и улучшить генерацию futures.</p><ul><li>Async-функция в Rust разворачивается в state machine: компилятор генерирует enum с одним состоянием на каждую точку await плюс три служебных — Unresumed (старт), Returned (после завершения) и Panicked (после пойманного catch_unwind).</li><li>Функция с двумя await-точками у автора разворачивается в 360 строк MIR против 23 у синхронного аналога — больше чем в 15 раз. Часть этого LLVM срежет, но не всё.</li><li>Замена panic! в состоянии Returned на возврат Pending в release-сборках даёт 2–5% экономии размера прошивки на embedded.</li><li>Ещё четыре оптимизации: убрать state machine у async-блоков без await-точек, заинлайнить futures с одним await, схлопнуть одинаковые состояния из ветвей match, под panic=abort вообще убрать состояние Panicked.</li><li>Автор подал <a href="https://github.com/rust-lang/rust-project-goals" rel="noopener">Rust Project Goal</a> и оценил работу примерно в €30 000 финансирования.</li></ul><h2>Что компилятор делает с async-блоком</h2><p>Возьмём простой пример из статьи:</p><p>У bar два await-points — значит, в state machine минимум два состояния. Просим компилятор сдампить MIR на этапе coroutine_resume (последний async-специфичный pass) и видим:</p><p>Returned и Panicked — служебные. Future::poll — безопасная функция, и опросить уже завершённую future нельзя так, чтобы получить undefined behaviour. Поэтому компилятор после первого Ready переключает future в Returned, и при повторном poll она паникует. Похожая логика для Panicked: после пойманного unwind future блокируется от повторного poll, иначе можно дёрнуть её в неконсистентном состоянии — автор сам отмечает, что точная документация по этому состоянию ему не нашлась, и сравнивает механизм с mutex poisoning.</p><p>Логично, но автор замечает: bar генерирует 360 строк MIR, а синхронный аналог — 23 строки. То есть в 15+ раз больше. Часть этого добавления LLVM оптимизирует позже, но не всё — и на embedded остаток ощутимо влияет на размер прошивки.</p><h2>Оптимизация 1: вместо panic — снова Pending</h2><p>Future в состоянии Returned <i>обязана</i> не вызывать UB. А <i>не обязана</i> паниковать. Можно вместо panic! просто возвращать Pending: контракт Future не нарушается, ничего опасного не происходит.</p><p>Panic — относительно дорого: добавляется ветка с побочным эффектом, которая плохо оптимизируется. Автор пропатчил компилятор и измерил: <b>2–5% сокращение размера бинарника</b> для async-прошивок на embedded.</p><p>Идеальное место — флаг по аналогии с overflow-checks = false: в debug-сборке оставляем panic, чтобы быстро ловить ошибочный повторный poll, в release — экономим. Аналогично, при panic = abort состояние Panicked потенциально можно убрать целиком — автор хочет это отдельно исследовать.</p><h2>Оптимизация 2: лишняя state machine у пустого async-блока</h2><p>Возвращаемся к foo: там нет ни одного await-point, future просто возвращает число. Логично было бы реализовать вручную как:</p><p>Никакого состояния не нужно — просто вернуть 5. На деле компилятор генерирует CoroutineLayout с тремя вариантами (Unresumed, Returned, Panicked) и честный switch по дискриминанту перед каждым возвратом — даже там, где без него можно было обойтись. Когда await-точек ноль, state machine можно убирать целиком — это <b>около 0,2% размера бинарника</b>. Поведение чуть меняется только для неконформных executor-ов: future всегда возвращает Ready без переходов между состояниями.</p><h2>Ещё две оптимизации в плане работ</h2><ul><li>Inlining futures с одним await-point. В таких future внутреннее состояние сводится к Suspend0 + Returned + Panicked, а ветка управления при правильной работе компилятора схлопывается в обычный вызов с одним переключением.</li><li>Схлопывание идентичных состояний из ветвей match. Если в функции несколько await-точек попадают в одинаковые по смыслу варианты (одни и те же сохранённые поля), их можно объединить в одно состояние state machine — без потери семантики.</li></ul><p>Каждый из этих кейсов даёт меньше, чем замена panic на Pending, но вместе с ней образуют связный набор патчей в компилятор. Автор формулирует план так: вынести проблему из «каждая embedded-команда со своими обходными приёмами» в «исправление в rustc раз и навсегда».</p><h2>Project Goal и финансирование</h2><p>Изменения требуют времени на проектирование, обсуждение в Rust-проекте и собственно правки. Автор оформил это как <a href="https://rust-lang.github.io/rust-project-goals/2026/async-statemachine-optimisation.html" rel="noopener">Project Goal по async state machine optimisation</a> и оценил необходимое финансирование примерно в <b>€30 000</b> — в основном на работу embedded-инженера над патчами компилятора. Платит обычно либо сам Tweede golf, либо Rust Foundation через программу грантов, либо отдельный спонсор; Project Goal — публичный механизм, чтобы заявить цель и собрать поддержку, в том числе финансовую. По меркам компиляторных проектов это недорого: типичная цена «уберём 2–5% размера прошивки в любом async-проекте» — десятки тысяч евро.</p><blockquote>Это вторая часть серии. В первой я показывал, что вы можете сделать в своём async-коде, чтобы избежать части bloat. Во второй мы лезем во внутренности и переводим методы из первой части в оптимизации для компилятора.</blockquote><h2>Выводы</h2><p>Async в Rust — давно главный болевой пункт у embedded-сообщества: обещания zero cost не подтверждаются, а обходные приёмы на уровне пользовательского кода сильно ограничены. Tweede golf формулирует разговор иначе: показывает, какие именно паттерны генерации виноваты, и предлагает их закрывать в компиляторе.</p><p>Для практика это значит две вещи. Первое — писать async-код можно не оглядываясь на «не сделать ли я ещё одно состояние»; bloat лежит в компиляторе, и просьба к нему — править генерацию, а не ваши async fn. Второе — следить за статусом Project Goal: если его подхватят, размер async-кода в release-сборках уменьшится у всех пользователей сразу, без правок в их репозиториях.</p><p>Оригинал — на <a href="https://tweedegolf.nl/en/blog/237/async-rust-never-left-the-mvp-state" rel="noopener">tweedegolf.nl/en/blog/237/async-rust-never-left-the-mvp-state</a>. Первая часть серии — <a href="https://tweedegolf.nl/en/blog/235/debloat-your-async-rust" rel="noopener">«Debloat your async Rust»</a>. Project Goal лежит на <a href="https://rust-lang.github.io/rust-project-goals/2026/async-statemachine-optimisation.html" rel="noopener">rust-project-goals/2026/async-statemachine-optimisation</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Inline-пропсы в React тихо обнуляют memo: разбор на 200 строк, как чинить</title>
      <link>https://tproger.ru/translations/inline-propsy-v-react-tiho-obnulyayut-memo-razbor-na-200-strok-k</link>
      <comments>https://tproger.ru/translations/inline-propsy-v-react-tiho-obnulyayut-memo-razbor-na-200-strok-k?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/inline-propsy-v-react-tiho-obnulyayut-memo-razbor-na-200-strok-k</guid>
      <description><![CDATA[<p>Контролируемый эксперимент LogRocket: 200 memoized React-строк, inline объекты в JSX делают коммит 244 мс на нажатие. Простой фикс снижает до 6 мс. Разбор.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/inline-propsy-v-react-tiho-obnulyayut-memo-razbor-na-200-strok-k">Inline-пропсы в React тихо обнуляют memo: разбор на 200 строк, как чинить</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 05 May 2026 11:30:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Совет по производительности React обычно сводится к набору рецептов: оборачиваем дорогие дочерние компоненты в React.memo, добавляем useCallback к обработчикам, useMemo к вычислениям — и идём дальше. На практике эти инструменты работают только тогда, когда передаваемые через них значения действительно стабильны. Если родитель пересоздаёт объект или функцию на каждом рендере, React видит новую ссылку и memoization-граница перестаёт делать полезную работу.</p><p>В <a href="https://blog.logrocket.com/react-pattern-everyone-uses-kills-performance/">материале на блоге LogRocket</a> разобран один из самых частых React-паттернов, который в неподходящем контексте оказывается одним из самых дорогих, — передача inline-объектов, массивов и колбэков прямо в месте вызова компонента. Контролируемый эксперимент с 200 memoized строк показывает, как это превращает один keystroke в коммит на 243,9 мс — и как простой рефактор возвращает время до 6 мс.</p><p><b>React.memo сравнивает по ссылке.</b> Внутри React работает Object.is: {padding:16} и {padding:16} — это разные ссылки, значит «изменилось».</p><p><b>Inline-объекты в JSX обнуляют memo.</b> Каждый рендер родителя создаёт новый объект/функцию, мемоизированный дочерний компонент видит «новые пропсы» и перерисовывается заново. Оптимизация, которую вы планировали, не срабатывает.</p><p><b>Цена в живом коде.</b> Эксперимент: 200 memoized строк, поиск, inline style и onAddToCart. После 6 нажатий — счётчик рендеров каждой строки = 14, на keystroke коммит 243,9 мс.</p><p><b>Фикс простой.</b> Статичный объект — в module scope. Динамический колбэк — в useCallback с пустыми зависимостями. После: коммит 6 мс, счётчик рендеров = 2, Why Did You Render молчит.</p><p><b>React Compiler не отменяет понимания.</b> Он автоматизирует много memoization, но useMemo/useCallback остаются escape hatch для случаев, когда нужен точный контроль (например, dependency для Effect).</p><h2>Как работает bailout у React</h2><p>React.memo оборачивает компонент в memoization-границу. Когда родитель ререндерится, React не пропускает дочерний автоматически только потому, что он мемоизирован. Вместо этого сравниваются новые пропсы со старыми. Если каждый пропс считается равным — bail out, переиспользуем предыдущий результат. Если хотя бы один не равен — рендер. По умолчанию React сравнивает попропсово через Object.is.</p><p>Эта деталь критична, потому что для объектов и функций Object.is — это, по сути, проверка по ссылке:</p><p>Содержимое выглядит идентичным, но ссылки разные. React поэтому считает их изменёнными. Именно по этой причине inline-объекты и колбэки оказываются скрытой причиной того, что мемоизированный дочерний компонент продолжает ререндериться.</p><p>Та же логика объясняет, зачем нужны useCallback и useMemo. По <a href="https://react.dev/reference/react/useCallback">документации React</a>, useCallback кеширует определение функции между рендерами, а useMemo — результат вычисления. Оба помогают только тогда, когда зависимости остаются достаточно стабильными, чтобы React переиспользовал предыдущее значение. Если положить в массив зависимостей нестабильный объект, React видит новую зависимость на каждом рендере и пересчитывает заново.</p><p>Это и объясняет, почему баг ощущается запутанным в реальном приложении. Значения «выглядят» неизменными для человека: у style-объекта те же ключи, тело колбэка идентично, config всё ещё говорит то же самое. Но React не сравнивает намерение или структуру — он сравнивает идентичность.</p><h2>Когда inline-пропсы реально становятся проблемой</h2><p>Стоит провести границу между теоретической и практической ценой. Inline-колбэк сам по себе — не баг производительности. Если ребёнок дешёвый, частота рендера низкая и memoization-границы вокруг нет, измеримого отрицательного эффекта может вообще не быть. И в документации React, и в гайдах LogRocket по производительности — общее место: оптимизация работает там, где есть реальные узкие места, а не гипотетические.</p><p>Проблема начинается, когда сходятся три условия одновременно:</p><ul><li>Родитель ререндерится часто (поиск, скролл, фильтры, анимация, live data).</li><li>Дочерний компонент или поддерево большое — лишняя работа заметна.</li><li>Вы уже добавили memoization и ждёте, что React пропустит работу, когда «ничего важного не изменилось».</li></ul><p>В такой связке нестабильные inline-ссылки не просто добавляют немного оверхеда — они <b>обнуляют ту оптимизацию, которую вы намеренно ввели</b>. И этот паттерн коварен в продакшене: он не объявляет себя багом. UI работает, исключений нет, предупреждений нет, и без профилировщика часто нет очевидного запаха. Цена проявляется иначе: тормозящая фильтрация списков, лаг ввода, шумные flame-графы и компонентные деревья, которые продолжают перерисовываться, даже когда осмысленные данные не менялись.</p><h2>Контролируемый эксперимент: 200 memoized строк</h2><p>Чтобы не спорить «плохо или нормально», автор поста собрал контролируемый тест: поисковый список товаров с 200 memoized строками, где каждая строка получает одинаковые логические значения, но новые ссылки на объект и функцию на каждом рендере родителя. Это позволяет напрямую увидеть, делает ли React.memo bail out или всё поддерево ререндерится на каждое нажатие клавиши.</p><p>Представьте storefront UI с 200 memoized ProductRow. Родитель — ProductList — хранит searchTerm в state. Каждое нажатие апдейтит state, ререндерит ProductList и снова прогоняет JSX, который мапает отфильтрованные товары. В эксперименте каждая ProductRow обёрнута в memo и помечена whyDidYouRender = true, но получает два inline-пропса в месте вызова:</p><p>Это ровно тот случай, о котором React предупреждает при передаче функций в memoized-компоненты: свежая функция или объект, созданные во время рендера, не пройдут сравнение пропсов, если ссылку не стабилизировать.</p><p>В эксперименте эффект становится виден почти мгновенно. Объект style и колбэк onAddToCart пересоздаются каждый раз, когда ререндерится ProductList, поэтому memo-обёртка видит изменённые пропсы для каждой строки на каждом нажатии. Счётчик рендеров делает это конкретным: после шести нажатий каждая видимая строка показывает <b>Renders: 14</b>. React Profiler затем показывает рантайм-цену ошибки: одно нажатие порождает коммит, в котором ProductList занимает <b>243,9 мс</b> и все 200 row-fibers светятся в flame-графе.</p><p>Здесь React Developer Tools отрабатывают по полной. По официальной документации, React DevTools позволяют инспектировать компоненты, редактировать пропсы и state, а также находить проблемы производительности. Профайлер также доступен программно через &lt;Profiler&gt;, но интерактивный вид DevTools обычно используется командами для отладки.</p><p><a href="https://github.com/welldone-software/why-did-you-render">Why Did You Render</a> делает корневую причину ещё нагляднее. Пакет модифицирует React и сообщает о потенциально избегаемых ререндерах. В этом примере он рапортует props.style как «different objects that are equal by value» и props.onAddToCart как «different functions with the same name» — ровно тот референциальный мисматч, который мы и ожидаем. Это диагностический инструмент только для разработки, не для прода, но крайне эффективный для выявления этого класса багов.</p><h2>Рефакторинг, который реально решает проблему</h2><p>Чтобы остановить каскад рендеров, нужны стабильные ссылки. Концептуально фикс прост: значения, которые не меняются, не должны пересоздаваться во время рендера; колбэки, которым нужно жить через рендеры, должны быть memoized, когда ребёнок зависит от референциальной стабильности.</p><p>Вынос ROW_STYLE в module scope решает проблему на самом дешёвом уровне: React никогда не видит новую ссылку на объект, потому что он создаётся один раз вне компонента. Использование useCallback для handleAddToCart даёт ребёнку стабильную ссылку на функцию между рендерами, пока массив зависимостей не меняется. Это и есть тот случай, для которого React документирует useCallback при передаче функций в мемоизированных детей.</p><p>В эксперименте стабилизация ссылок восстанавливает bail-out. Измеренный результат впечатляет: ProductList падает с 243,9 мс до <b>6 мс</b>, бейджи рендеров остаются на <b>2</b> сколько ни печатай, и Why Did You Render замолкает — избегаемых референциальных мисматчей больше нет.</p><h2>Когда стабилизировать ссылки, а когда — нет</h2><p>Это часть, которая часто теряется в дискуссиях про производительность. Урок не «никогда не используй inline-объекты» и не «оборачивай всё в useCallback». Урок: <b>memoization — это контракт</b>. Если ребёнок полагается на референциальное равенство, чтобы пропустить работу, родитель обязан этот контракт уважать и передавать стабильные ссылки.</p><p>Но это не значит, что каждому компоненту нужна агрессивная мемоизация. Современные рекомендации React всё ещё трактуют memoization как точечную оптимизацию, а не дефолтный стиль. Если рендер дешёвый, поддерево маленькое или ребёнок не memoized, стабилизация ссылок может добавить сложности без реальной выгоды. Поэтому большинство гайдов по React performance, включая обзоры LogRocket, акцентируют профилировку прежде, чем механически оптимизировать.</p><p>Полезное правило большого пальца: <b>сначала перенеси, потом мемоизируй</b>. Если значение статично — вынеси его за пределы тела компонента, прежде чем тянуться к хукам. Это даёт референциальную стабильность почти без когнитивной или рантайм-нагрузки. useCallback и useMemo — только когда значение реально динамическое и ребёнок выигрывает от стабильной идентичности.</p><h2>Меняет ли это React Compiler</h2><p>Один актуальный нюанс — <a href="https://react.dev/learn/react-compiler">React Compiler</a>. Документация описывает его как стабильный билд-тайм инструмент, который автоматически оптимизирует React-приложения и по умолчанию мемоизирует код на основе анализа и эвристик. Это уменьшает потребность в ручных useMemo, useCallback и React.memo, особенно в новом коде.</p><p>Но это не делает референциальную стабильность нерелевантной. Документация также отмечает, что useMemo и useCallback остаются полезными как escape hatch для случаев, когда разработчику нужен точный контроль — например, чтобы держать memoized-значение стабильным как зависимость для Effect. Так что даже в кодовых базах, переходящих на React Compiler, всё равно полезно понимать, как нестабильные ссылки влияют на ререндеры, результаты профайлера и кейсы, где ручной контроль ещё нужен.</p><h2>Итог</h2><p>Inline-объекты и inline-колбэки — не плохой React-код по умолчанию. Чаще всего это просто обычные JavaScript-выражения внутри JSX. Проблема появляется, когда они пересекают memoization-границу и вы ждёте, что React воспримет «то же значение» как «тот же пропс». По умолчанию React сравнивает пропсы и зависимости хуков через Object.is, поэтому для объектов и функций новой ссылки достаточно, чтобы React счёл значение изменившимся.</p><p>Этот паттерн заслуживает большего внимания, чем обычно получает. Это не пустяковая микрооптимизация. Это один из самых простых способов случайно обнулить React.memo — особенно в фильтруемых списках, дашбордах, search-heavy UI и компонентных деревьях с дорогими потомками. Код выглядит чистым, приложение работает, но оптимизация, на которую вы рассчитывали, исчезает.</p><p>Практический вывод для команд, строящих быстрые React-интерфейсы: <b>профилируй сначала</b>. Если memoized поддерево всё ещё ререндерится слишком часто — проверь пропсы прежде, чем винить React. Перенеси статичные объекты из render-пути. Мемоизируйте колбэки только тогда, когда ребёнок реально выигрывает. Используй React DevTools и Why Did You Render, чтобы подтвердить, что именно изменилось и почему. Делай это последовательно — и React.memo перестанет быть декоративным куском кода и начнёт делать свою работу.</p><p>Источник: <a href="https://blog.logrocket.com/react-pattern-everyone-uses-kills-performance/">The React pattern everyone uses that quietly kills performance — LogRocket Blog</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Box в Rust: как уменьшить память программы с 895 до 420 МБ</title>
      <link>https://tproger.ru/translations/box-v-rust-kak-umenwit-pamyat-programmy-s-895-do-420-mb</link>
      <comments>https://tproger.ru/translations/box-v-rust-kak-umenwit-pamyat-programmy-s-895-do-420-mb?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/box-v-rust-kak-umenwit-pamyat-programmy-s-895-do-420-mb</guid>
      <description><![CDATA[<p>Перевод dystroy.org: как заменой Option на Option&gt; и кастомным Deserializer уменьшить память Rust-программы с 895 до 420 МБ. Читайте разбор с кодом.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/box-v-rust-kak-umenwit-pamyat-programmy-s-895-do-420-mb">Box в Rust: как уменьшить память программы с 895 до 420 МБ</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 27 Apr 2026 10:54:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если ваши Rust-структуры с Option&lt;BigStruct&gt; заполнены процентов на 20–30 — следующие 5 минут вероятно вернут вам сотни мегабайт RAM. Разработчик <a href="https://dystroy.org/">Дени «Canop» де Майе</a> (автор файлового менеджера <a href="https://github.com/Canop/broot">broot</a>) сократил потребление памяти своей программы <b>с 895 МБ до 420 МБ — это минус 475 МБ</b>. Изменил только layout структур и стратегию десериализации serde. Перевод <a href="https://dystroy.org/blog/box-to-save-memory/">его статьи</a> с пояснениями.</p><p>Главная идея: в Rust поле типа Option&lt;BigStruct&gt; занимает <i>столько же</i>, сколько BigStruct — даже когда оно None. Чтобы пустое поле «весило одно слово», нужно обернуть значение в Box. Компилятор тогда применяет niche-оптимизацию (использует нулевой указатель как маркер None) — Option&lt;Box&lt;T&gt;&gt; весит ровно как один указатель. На задачах с массой опциональных полей это даёт двукратное сокращение памяти.</p><ul><li>Кейс: десериализация всех JSON-моделей AWS SDK (Smithy Shapes) через serde в Rust</li><li>Было: 895 МБ. Стало: 420 МБ. Экономия 475 МБ (-53%)</li><li>Изменения: только layout структур + кастомный Deserializer для пустых полей</li><li>Ключевая мысль: Option&lt;Big&gt; ≠ Option&lt;Box&lt;Big&gt;&gt; по памяти</li><li>Niche-оптимизация компилятора: Option&lt;Box&lt;T&gt;&gt; весит как один указатель</li><li>Verification: jemalloc через feature-флаг profile, замер до/после загрузки</li><li>Цена: десериализация чуть дороже по CPU (значение всё равно создаётся, потом отбрасывается)</li></ul><h2>Реальный кейс</h2><p>Программа автора десериализует все JSON-файлы из репозитория <a href="https://github.com/awslabs/aws-sdk-rust/tree/main/aws-models">awslabs/aws-sdk-rust/aws-models</a> в структуры «Smithy Shape». Эти файлы содержат тысячи объявлений вроде такого:</p><p>Как принято в Rust-коде, для разбора используется serde. Соответствующие структуры выглядят естественно — это обычная вложенность структур со множеством опциональных полей и serde-атрибутов. Не нужно вчитываться в код: обратите внимание только на сами имена полей и на россыпь Option&lt;...&gt;:</p><p>Это типичный для Rust код. Но именно такой layout приводит к тому, что после десериализации структуры занимают 895 МБ. При этом анализ показывает: большинство опциональных строк отсутствуют — это и стало рычагом для радикального сокращения объёма. Чтобы понять механизм, нужен короткий теоретический отступ.</p><h2>Как устроены структуры в памяти Rust</h2><p>На 64-битной платформе одно «слово» — это 8 байт. Например, столько занимает usize.</p><p>Тип String занимает 3 слова: указатель на буфер, длину и ёмкость (capacity). Это <b>24 байта</b> на сам тип String (проверить можно через dbg!(std::mem::size_of::&lt;String&gt;())) — не считая место под содержимое строки на heap. Подробности в <a href="https://doc.rust-lang.org/std/string/struct.String.html">документации Rust</a>.</p><p>Существует niche-оптимизация компилятора: Option&lt;String&gt; весит столько же, сколько String. Дополнительный байт под пометку «это None» не нужен — пустое значение определяется по нулевому указателю.</p><p>Поэтому такая структура, когда все строки None, занимает ровно <b>120 байт</b> (5 × 24):</p><h3>Композиция структур</h3><p>Дальше — самое важное. Что будет, если такая структура встроена в другую? Пусть это будет Container1, у которой одно поле Option&lt;String&gt; и одно — SmithyServiceTrait:</p><p>Размер Container1 — 24 + 120 = 144 байта: 24 под Option&lt;String&gt; и 120 под SmithyServiceTrait. А что, если поле trait сделать опциональным — на случай, когда trait отсутствует?</p><p>Сколько занимает Container2, когда оба поля None? <b>Те же 144 байта.</b> Опциональность ничего не сэкономила: место под структуру уже зарезервировано в layout родителя, и компилятор всё равно держит его готовым. Везёт ещё, что внутри SmithyServiceTrait только поля Option&lt;String&gt; — это позволяет компилятору не добавлять дополнительный байт-discriminant для внешнего Option.</p><p>Применив эту арифметику к SmithyTraits, видно, почему наивная реализация так разбухает в памяти.</p><h3>Почему в Java и других языках с GC такой проблемы нет</h3><p>В языках с reference-семантикой полей композиция работает иначе. Когда у класса Container есть поле-объект, это поле — указатель на heap. Если поле null, оно занимает только одно слово.</p><p>Чтобы добиться того же поведения в Rust — то есть чтобы пустое поле занимало одно слово, — нужно явно вынести содержимое на heap, обернув в Box:</p><p>Теперь, когда оба поля None, Container3 занимает всего <b>32 байта</b>: 3 слова под Option&lt;String&gt; и одно слово под Option&lt;Box&lt;...&gt;&gt;. Та же niche-оптимизация работает и здесь: Option&lt;Box&lt;...&gt;&gt; весит столько же, сколько голый Box&lt;...&gt;.</p><h2>Что именно поменялось</h2><p>Изменение свелось к трём шагам:</p><ol><li>Найти структуры, которые часто оказываются «пустыми» (все поля — None).</li><li>Сделать их опциональными в родительской структуре, переместив на heap через Box.</li><li>Написать кастомный Deserializer, чтобы пустые структуры вообще не попадали в результат.</li></ol><p>Было:</p><p>Стало:</p><p>Аналогично, в SmithyShape все поля Option&lt;SmithyReference&gt; заменены на Option&lt;Box&lt;SmithyReference&gt;&gt;. Местами пришлось поправить акцессоры, потому что путь к данным удлинился на одну разыменовку. И всё — память на хранение десериализованных AWS-shape снизилась вдвое, экономия 475 МБ.</p><h3>Подводные камни</h3><ul><li><b>CPU чуть дороже.</b> Десериализация полного объекта всё равно происходит — а потом он отбрасывается, если оказался пустым. Парадокс: суммарно задача стала <i>быстрее</i>, потому что меньше памяти = меньше работы аллокатора, меньше TLB-промахов, лучше попадания в L2/L3-кеш. На больших объёмах эти эффекты перевешивают накладные расходы на лишний разбор.</li><li><b>Фрагментация heap.</b> Много мелких Box ведёт к фрагментированной куче. В авторском случае это не было проблемой, но в долгоживущих сервисах — стоит держать в уме.</li></ul><h2>Как проверить, что экономия реальная</h2><p>Чутьё подсказывает, где можно сэкономить и сколько примерно. Но без замеров — не работа. В Rust нет простого способа узнать, сколько весит композитный объект со всеми полями и аллокациями за указателями. Решение автора — использовать аллокатор, умеющий отдавать статистику. В стандартной поставке такие данные ограничены, поэтому подключается <a href="https://github.com/tikv/jemallocator">jemalloc</a>:</p><p>В Cargo.toml объявляется feature-флаг profile, чтобы не таскать jemalloc в обычные сборки:</p><p>В main.rs аллокатор подключается под этим же флагом:</p><p>И в самой функции загрузки структур делается замер: до и после.</p><p>Совет автора: tikv_jemalloc_ctl отдаёт ещё кучу деталей, которые полезно мониторить в серверных приложениях.</p><h2>Что запомнить из статьи</h2><ul><li>Применяйте Option&lt;Box&lt;T&gt;&gt; только там, где поле пусто в большинстве случаев — иначе heap-fragmentation и разыменовка перевесят выигрыш.</li><li>Размер типа узнать просто: std::mem::size_of::&lt;T&gt;(). Реальное потребление с heap — только через jemalloc-ctl, dhat-rs или heaptrack.</li><li>Niche-оптимизация работает не для всего: String, Box, Vec, &amp;T — да; кастомный enum без явных #[repr] — обычно нет.</li><li>При desered-через-serde: голая замена типа на Option&lt;Box&gt; не даёт выигрыша — нужен кастомный Deserializer, который вернёт None для «пустых» структур.</li><li>Перед оптимизацией — всегда замер. После оптимизации — снова замер. Без цифр это карго-культ.</li></ul><h2>От редакции</h2><p>Один практический совет от редакции: чтобы быстро найти кандидатов на оптимизацию в большом проекте, прогоните код через <a href="https://github.com/nnethercote/dhat-rs">dhat-rs</a> — это профайлер аллокаций от автора «Rust Performance Book». Он покажет, какие типы съедают больше всего heap. Альтернатива Option&lt;Box&lt;T&gt;&gt; — <a href="https://docs.rs/bumpalo/">arena-аллокаторы</a> вроде bumpalo: если вся группа объектов живёт один пакетный жизненный цикл, разовый bump-аллокатор может оказаться эффективнее множества мелких Box.</p><p>Если интересна тема Rust в крупных продакшенах, у Tproger есть свежий материал про то, как <a href="https://tproger.ru/news/-whatsapp-perepisal-mediadvizhok-na-rust-i-vykinul-160-tysyach-stro">WhatsApp переписал медиадвижок на Rust и выкинул 160 тысяч строк C++</a> — там тоже про экономию памяти и скорость, только в другом масштабе.</p><p>Источник: <a href="https://dystroy.org/blog/box-to-save-memory/">«Box to save memory» — dystroy.org/blog</a>. Автор оригинала — <a href="https://dystroy.org/">Дени «Canop» де Майе</a>, разработчик файлового менеджера <a href="https://github.com/Canop/broot">broot</a> и других open-source утилит на Rust.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как сделать быстрый интерпретатор динамического языка — перевод статьи Филипа Пизло</title>
      <link>https://tproger.ru/translations/kak-sdelat-bystryj-interpretator-dinamicheskogo-yazyka-perevod</link>
      <comments>https://tproger.ru/translations/kak-sdelat-bystryj-interpretator-dinamicheskogo-yazyka-perevod?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/kak-sdelat-bystryj-interpretator-dinamicheskogo-yazyka-perevod</guid>
      <description><![CDATA[<p>Перевод статьи Филипа Пизло (WebKit FTL JIT): 21 оптимизация AST-интерпретатора без JIT, байткода и SSA. От 35x медленнее CPython до 1,9x быстрее.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/kak-sdelat-bystryj-interpretator-dinamicheskogo-yazyka-perevod">Как сделать быстрый интерпретатор динамического языка — перевод статьи Филипа Пизло</a>»</p>]]></description>
      <category><![CDATA[C++]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 22 Apr 2026 16:30:05 GMT</pubDate>
      <content:encoded><![CDATA[<p>Хотите ускорить рантайм своего маленького DSL или учебного языка — но писать JIT нет сил, а байткод-компилятор кажется отдельным проектом? Перевод <a href="https://zef-lang.dev/implementation" rel="nofollow">статьи Филипа Пизло</a> (автора JavaScriptCore-FTL, B3 и Riptide GC в WebKit) показывает, как простой AST-интерпретатор для собственного языка Zef он разогнал <b>в 16,6 раза</b> без единой строчки машинного кода, а с переходом на обычный C++ — в <b>67 раз</b>.</p><p>Пизло — человек, от которого обычно ждут постов про «как мы выкрутили очередной трюк в JIT JavaScriptCore». Его прошлые тексты — это <a href="https://webkit.org/blog/3362/introducing-the-webkit-ftl-jit/" rel="nofollow">FTL JIT</a>, <a href="https://webkit.org/blog/5852/introducing-the-b3-jit-compiler/" rel="nofollow">B3</a>, <a href="https://webkit.org/blog/7122/introducing-riptide-webkits-retreating-wavefront-concurrent-garbage-collector/" rel="nofollow">Riptide GC</a> и другие вещи, которые требуют двух лет опыта в компиляторах. Но этот текст — принципиально другой. Он показывает ровно то, о чём обычно не пишут: что делать, когда у вас ещё нет JIT, а GC ещё не является главной болью.</p><p>Стартовая версия интерпретатора Zef была <b>в 35 раз медленнее CPython 3.10</b>, в 80 раз медленнее Lua 5.4.7 и в 23 раза медленнее QuickJS-ng 0.14.0. После 21 оптимизации (<i>ни одной из которых не требует SSA-преобразований (static single assignment), переделки сборщика мусора, байткода или машинного кода</i>) тот же интерпретатор проигрывает CPython всего в 2,1 раза, Lua — в 4,8 раза, QuickJS — в 1,35 раза. Если же собрать тем же обычным C++ без Fil-C — Zef становится <b>быстрее CPython в 1,9 раза и быстрее QuickJS в 3 раза</b>.</p><p><b>Представление значений</b>: tagged 64-битные значения с NaN-boxing (упаковка целых и указателей в неиспользуемые биты double) позволяют держать числа на стеке и отличать их от указателей по битам.</p><p><b>Symbols вместо строк</b>: один раз интернировать все имена методов и переменных в Symbol*, потом сравнивать указатели — даёт +18%.</p><p><b>Object Model + Inline Caches + Watchpoints</b>: самый большой прирост (+355%, 4,55x) — одна мегакоммит-оптимизация, без которой остальное не работает.</p><p><b>Специализация мелочей</b>: выделенный AST-узел для a + b вместо строкового a.add(b), Getter/Setter-инференс, специализированные ZeroArguments/OneArgument/TwoArguments.</p><p><b>Штраф Fil-C — 4x</b>: memory-safe C, в котором автор пишет, даёт GC бесплатно, но накладывает штраф; итоговый буст после перехода на обычный C++ — 67x.</p><h2>О каком интерпретаторе речь</h2><p>Zef — это учебный динамический язык, который Пизло написал для удовольствия. В нём есть классы с наследованием, приватные поля (по умолчанию), замыкания, кастомные операторы, getter'ы и setter'ы. На уровне идиом он похож на Ruby и тихую версию JavaScript. Исходный интерпретатор — <a href="https://zef-lang.dev/source-viewers/baseline.html" rel="nofollow">наивный AST-walker</a>, в котором каждая нода — это C++-объект с виртуальным методом Node::evaluate, а все переменные живут в std::unordered_map&lt;std::string, Value&gt;.</p><p>Весь исходный код интерпретатора, diff-просмотрщики для каждой оптимизации и бенчмарки выложены на <a href="https://zef-lang.dev/implementation" rel="nofollow">zef-lang.dev/implementation</a>. Бенчмарки — набор ScriptBench1: Richards (OS scheduler), DeltaBlue (constraint solver), N-Body (физика) и Splay (самобалансирующееся двоичное дерево). Все портированы на Zef, Lua, Python и JavaScript.</p><p>Одна деталь про измерения: Пизло собирает свой интерпретатор компилятором <a href="https://fil-c.org/" rel="nofollow">Fil-C++</a> — это его собственный memory-safe C, который даёт сборщик мусора из коробки, но платит за это примерно 4x производительности. То есть когда в тексте написано «в 35 раз медленнее CPython», держите в голове, что 4x из этого — стоимость безопасности памяти, а остальные ~9x — это то, что будем выжимать.</p><h2>Исходная точка: в 35 раз медленнее Python</h2><p>В базовом варианте Пизло сделал два единственных осознанных про производительность выбора:</p><ul><li><b>64-битное tagged-представление значений</b>. Значение может быть int32, double или Object*. Double'ы сдвигаются на 0x1000000000000 — техника из JavaScriptCore, которую в литературе зовут NaN-boxing. Целые и указатели лежат как есть. Это позволяет определить тип значения битовой проверкой и не выделять int'ы в куче.</li><li><b>Язык — C++</b>. Пизло обосновывает: «Java — потолок низкоуровневых оптимизаций слишком низкий, Rust — сборщик мусора требует глобального изменяемого состояния и циклических ссылок, с этим придётся драться или писать много unsafe».</li></ul><p>Всё остальное — сознательные «плохие» решения ради скорости разработки:</p><ul><li>Fil-C++ — штраф 4x, но GC бесплатно.</li><li>Рекурсивный AST-интерпретатор через виртуальные вызовы.</li><li>std::string везде — для имён переменных, методов, полей.</li><li>std::unordered_map везде — на каждый Get переменной идёт хеш-лукап по строке.</li><li>Цепочки рекурсивных вызовов по scope chain: класс внутри функции внутри класса внутри функции — каждый доступ проходит через несколько уровней вызовов.</li></ul><p>Результат — <b>35x медленнее CPython, 80x медленнее Lua, 23x медленнее QuickJS</b>. Дальше — что с этим можно сделать, <i>не переписывая интерпретатор на JIT</i>.</p><h2>#1–2: Прямой вызов операторов и RMW</h2><p>В Zef выражение a + b эквивалентно a.add(b). Наивный парсер делает одну и ту же AST-ноду для обоих случаев — DotCall(a, "add", [b]). Каждый плюс превращается в лукап строки "add" в Value::callMethod, который каскадом сравнивает её со всеми возможными именами операторов.</p><p>Первая оптимизация — парсер генерирует отдельные ноды Binary&lt;&gt; и Unary&lt;&gt;, каждая со своим виртуальным evaluate, который сразу зовёт Value::add, Value::sub и так далее. Без строковых сравнений на каждый плюс.</p><p><b>+17,5%</b>. Вторая оптимизация делает то же самое для += и аналогов — отдельные ноды SetRMW, DotSetRMW, SubscriptRMW. <b>+3,7%</b>. Итог: 1,22x быстрее старта.</p><h2>#3: Убрать проверку IntObject из быстрого пути</h2><p>В базовой версии Value различал четыре случая: tagged int32, tagged double, IntObject (для int64, не влезающих в 32 бита) и всё остальное. Фастпас вызывал isInt(), который через виртуальный вызов спрашивал Object::isInt() — на случай, если под обложкой лежит IntObject.</p><p>Правка — пусть Value работает только с int32 и double, а весь int64-код уезжает внутрь IntObject. <b>+1%</b>. Мелочь, но копеечка.</p><h2>#4: Symbols вместо std::string</h2><p>Это первый по-настоящему большой прирост. В исходном интерпретаторе std::string — это ключ везде, где есть хеш-лукап: локальные переменные, dot-доступ, имена операторов, имена методов. То есть на каждый x + 1 идут хеширование строки "x", strcmp на каждой коллизии, ещё раз для "+".</p><p>Оптимизация — классический interning. Новый класс Symbol. Строки превращаются в Symbol* через глобальный hash-consing — одна строка = один указатель. Равенство Symbol* теперь проверяется сравнением указателей, а хеш-таблицы ключуются по Symbol*, а не по std::string.</p><p>Правка большая по количеству затронутых файлов, но по сути тривиальная — меняются сигнатуры функций с const std::string&amp; на Symbol*. <b>+18%</b>. Суммарно: 1,46x быстрее старта.</p><h2>#5: Value Inline</h2><p>Простая техника: вынести часть методов Value в отдельный заголовочник valueinlines.h, чтобы они видели типы, которые нельзя было включить в value.h из-за циркулярных зависимостей. Компилятор теперь может их инлайнить в горячих местах. <b>+2,8%</b>.</p><h2>#6: Объектная модель + inline caches + watchpoints — 4,55x одним махом</h2><p>Самая большая оптимизация в статье, и единственная, которую Пизло специально защищает:</p><blockquote>Иногда единственный способ сделать реализацию языка быстрее — это посадить большой патч. Не позволяйте никому говорить, что хороший инженерный труд бывает только в маленьких удобоваримых изменениях. Это не всегда так. И точно не так, если вы хотите иметь быструю реализацию динамического языка.</blockquote><p>Патч объединяет три независимо бесполезных вещи:</p><h3>Новая объектная модель: Storage + Offsets</h3><p>В исходной версии каждый lexical scope выделял объект Context, а внутри Context жил hashmap с «полями» — локальными переменными. Объекты пользовательских классов были хуже: каждый объект содержал hashmap «класс → Context», потому что если Bar наследует Foo, поля у них приватные и могут иметь одинаковые имена — нужно различать, к какому классу относится поле.</p><p>Новая идея — Storage, который держит данные по Offsets, вычисленным на этапе resolve AST. Context всё ещё существует, но создаётся заранее, а объекты на рантайме просто выделяют Storage размера, который Context уже посчитал. Доступ к полю становится индексом по offset'у, а не лукапом в хеш-таблице.</p><h3>Inline caches</h3><p>Классическая техника — обычно её описывают в контексте JIT-компиляторов. Но здесь Пизло применяет её в <i>интерпретаторе</i>: для каждой AST-ноды типа expr.name запоминается тип, который expr имел в прошлый раз, и offset, куда резолвилось name. Запоминание — через placement-new на месте старой ноды: под той же памятью лежит новая специализированная нода, которая умеет быстрый путь.</p><p>Специализация может быть: «прямой load из Storage» (для локальной переменной), «проверка класса + direct call в функцию, которую видели в прошлый раз» (для метода). Плюс chain steps и watchpoints, если access требовал обхода scope chain.</p><h3>Watchpoints</h3><p>Представим: класс Foo внутри lexical scope, в котором есть переменная x. Метод Foo обращается к x. Внутри Foo имени x нет. Казалось бы, можно делать доступ без проверок. Но кто-то может унаследовать Foo и добавить геттер с именем x — тогда доступ должен пойти в геттер.</p><p>Watchpoint — это пассивный охранник: «если кто-то переопределил имя x в подклассе, инвалидируй мой inline cache». Пока имя не переопределяли, доступ работает на минимальных циклах.</p><h3>Почему всё вместе</h3><p>Пизло объясняет, почему эти три вещи не разделить:</p><ul><li>Новая объектная модель без inline caches не даёт никакого прироста.</li><li>Inline caches без watchpoints не могут кешировать большинство интересных случаев — слишком много условий.</li><li>И объектная модель, и watchpoints должны работать вместе с самого начала.</li></ul><p><b>+355% — 4,55x быстрее</b>. После этого Zef в 5,2 раза медленнее CPython, в 11,7 раза медленнее Lua, в 3,3 раза медленнее QuickJS. То есть накладные расходы Fil-C++ (примерно 4x) — это уже почти вся разница. Суммарно: 6,8x быстрее старта.</p><h2>#7: Arguments — свой тип вместо std::vector</h2><p>Раньше аргументы функций передавались как const std::optional&lt;std::vector&lt;Value&gt;&gt;&amp;. optional нужен, чтобы отличать геттер-вызов o.getter от функции без аргументов o.function() — в Zef это разные вещи для некоторых типов. То есть каждый вызов аллоцировал std::vector, копировал его, и ещё раз копировал в scope функции.</p><p>Правка — новый тип Arguments, физически совпадающий с layout scope, который callee создаст. Caller выделяет его напрямую, callee не копирует. Плюс в Fil-C++ std::optional всегда живёт в куче из-за специфики invisicaps — это отдельный штраф. <b>+33%</b>.</p><h2>#8–10: Специализация геттеров, сеттеров и callMethod</h2><p>Много методов в Zef устроены тривиально:</p><p>Это запись readable f. Метод fn f f — просто чтение переменной. Гонять это через полноценный eval AST'а расточительно. Оптимизация #8 — инференс: если тело функции — это просто Get, заменяем UserFunction на специализированный GetterFunction, делающий load по известному offset'у. <b>+5,6%</b>.</p><p>Оптимизация #9 — то же самое для сеттеров (fn set_x(v) x = v). Инференс сложнее — нужно матчить параметр сеттера как источник присваивания. <b>+3,4%</b>. Оптимизация #10 — тривиальная однострочная: inline-инг callMethod в заголовочник. <b>+3,2%</b>. Суммарно: 10,2x быстрее старта.</p><h2>#11: Глобальная hashtable для method dispatch</h2><p>Когда inline cache промахивается, старый путь лез через ClassObject::tryCallMethod и tryCallMethodDirect, которые делают два хеш-лукапа на каждый уровень иерархии наследования: «есть ли метод в этом классе» и «есть ли вложенный класс с этим именем». O(depth × 2).</p><p>Правка — глобальный hashmap (ClassObject*, Symbol*) → callee, который запоминает все успешные резолвы. Промах по IC теперь — один лукап в эту таблицу, и только если её нет — полный медленный путь. <b>+15%</b>. Суммарно: 11,8x.</p><h2>#12: Избавиться от std::optional на горячем пути</h2><p>В Fil-C++ std::optional почти всегда аллоцируется в куче — из-за особенностей union-типов и invisicaps. LLVM может непредсказуемо потерять capabilities указателей внутри union'ов; Fil-C++ компилятор страхуется и вставляет интринсики, которые заставляют всё, что выглядит как union, аллоцировать на heap.</p><p>Правка — обход кода, ведущего к std::optional на горячем пути. <b>+1,7%</b>. Важный сайд-эффект для тех, кто пишет на Fil-C++: избегайте union-типов в горячем коде.</p><h2>#13: ZeroArguments, OneArgument, TwoArguments</h2><p>Большинство встроенных функций Zef берут 0, 1 или 2 аргумента. Им не нужен полноценный Arguments-объект — он просто занимает память. Три новых типа: ZeroArguments, OneArgument, TwoArguments. Функции шаблонизируются и инстанцируются отдельно для каждого.</p><p>ZeroArguments нужен отдельно, потому что (Arguments*)nullptr уже используется для геттер-вызова. Теперь ZeroArguments означает «функция без аргументов». <b>+3,8%</b>.</p><h2>#14: Slow paths Value через static + by value</h2><p>Раньше медленные пути Value были member-функциями и принимали неявный const Value*. Значит, caller должен был stack-аллоцировать Value. В Fil-C++ любая stack-аллокация это heap-аллокация.</p><p>Правка — сделать эти методы static и принимать Value по значению. Аллокации не нужны. <b>+10%</b>. Суммарно: 13,6x. Это ещё одна «специфичная для Fil-C++» оптимизация, которая в Yolo-C++ (обычный C++) была бы бесплатной по умолчанию.</p><h2>#15–17: Дедупликация, быстрый sqrt и toString</h2><p>#15 — «оптимизация» в кавычках: убрать дублированный код в DotSetRMW. Пизло надеялся на прирост от уменьшения кода. Прироста нет — но код чище.</p><p>#16 — специализация Dot-ноды для value.sqrt. Inline caches хороши для объектов, но для числовых примитивов они не работают — там фастпас другой, через Binary&lt;&gt;/Unary&lt;&gt;. sqrt в базовом варианте летел через общий путь. Теперь — свой специализированный код. <b>+1,6%</b>.</p><p>#17 — то же самое для toString. Плюс чуть меньше аллокаций при конвертации int → string. <b>+2,7%</b>. Суммарно: 14,2x.</p><h2>#18: Специализация литералов массивов</h2><p>Код вроде my whatever = [1, 2, 3] по-старому каждый раз спускался в AST и вычислял 1, 2, 3. Но если литерал константный — результат известен на парсинге.</p><p>Оптимизация — специализированная ArrayLiteral-нода, которая при вычислении просто копирует заготовленный массив значений. <b>+8,1%</b>. Суммарно: 15,35x.</p><h2>#19: Value::callOperator по значению</h2><p>Та же идея, что в #14 — не передавать Value по ссылке в медленный путь callOperator, а принимать его по значению. Ещё одна Fil-C++-специфичная аллокация убирается. <b>+6,5%</b>. Суммарно: 16,3x.</p><h2>#20–21: Настройки компилятора и отключение ассертов</h2><p>#20 — билд-система: отключить RTTI и libc++ hardening, потому что Fil-C++ их всё равно перекрывает. Ни одного изменения в C++-коде, только флаги сборки. <b>+1,8%</b>.</p><p>#21 — отключить по умолчанию ассерты. Код использовал макрос ZASSERT (всегда проверяет). Замена на ASSERT (только если ASSERTS_ENABLED) должна была дать прирост, но — не дала. Зато готовит код к сборке в обычном C++.</p><p><b>После 21 оптимизации: 16,6x быстрее старта</b>. Zef в 2,1x медленнее CPython, в 4,8x медленнее Lua, в 1,35x медленнее QuickJS. Стартовали с 35x, 80x и 23x разрывов — оказались в окрестностях конкурентов, без строчки JIT.</p><h2>Переход на обычный C++ — ещё 4x</h2><p>Финальный эксперимент — собрать тот же код обычным C++ (GCC 11.4.0) вместо Fil-C++. Пизло называет это Yolo-C++ — без гарантий безопасности памяти. Получается <b>ещё 4x</b>. Суммарно: <b>67x быстрее базовой версии</b>. Zef в 1,9x <i>быстрее</i> CPython 3.10, в 1,2x медленнее Lua и в 3x быстрее QuickJS.</p><p>Сборка неидеальна: вместо Fil-C++ GC там calloc, память не освобождается — интерпретатор течёт, просто ScriptBench1 слишком короткий, чтобы это заметить. С реальным GC Yolo-C++-версия была бы ещё быстрее.</p><blockquote>В этот момент Zef в 1,9 раза быстрее CPython 3.10, в 1,2 раза медленнее Lua 5.4.7 и в 3 раза быстрее QuickJS-ng 0.14.0. Мы стали в 67 раз быстрее, чем там, где начали.</blockquote><h2>Итоговая таблица всех оптимизаций</h2><p>Что получается по ходу работы — в одной таблице (геосреднее по четырём бенчмаркам, чем быстрее, тем лучше):</p><h2>Выводы</h2><p>Главное в этом тексте Пизло — не сами оптимизации, а то, что они <i>все простые</i>. SSA здесь нет. GC переделывать не надо. Байткода нет. Машинного кода нет. Есть базовые вещи: не хешировать строки (Symbols), не аллоцировать лишнее (Value by value, специализированные Arguments), не ходить по hashmap'ам (Offsets + IC), запомнить то, что уже видели (inline caches), инвалидировать кеш только когда реально что-то поменялось (watchpoints).</p><p>Для тех, кто пишет интерпретатор с нуля — список в статье Пизло работает как чеклист. Первые оптимизации (#1–#5) дают суммарные 1,5x — они дешёвые. #6 даёт 4,55x — это день работы, но переворачивает игру. Всё остальное — серия из 3–8% приростов, которые в сумме добирают разницу.</p><p>Для тех, кто хочет потом написать JIT — эта статья объясняет, <i>как выглядит база</i>, на которую JIT потом ложится без сюрпризов. NaN-boxing, новая объектная модель, IC и watchpoints — это ровно то, что должно быть у интерпретатора до того, как вы начнёте генерировать машинный код.</p><p>Оригинал: <a href="https://zef-lang.dev/implementation" rel="nofollow">How To Make a Fast Dynamic Language Interpreter</a>, Filip Pizlo, 21 апреля 2026 года. Автор — один из ключевых инженеров WebKit JavaScriptCore, автор FTL JIT, B3, Riptide GC.</p>]]></content:encoded>
    </item>
    <item>
      <title>Mintlify заменила RAG на виртуальную файловую систему — и получила ускорение в 460 раз</title>
      <link>https://tproger.ru/translations/mintlify-zamenila-rag-na-virtualnuyu-fajlovuyu-sistemu-i-poluchi</link>
      <comments>https://tproger.ru/translations/mintlify-zamenila-rag-na-virtualnuyu-fajlovuyu-sistemu-i-poluchi?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/mintlify-zamenila-rag-na-virtualnuyu-fajlovuyu-sistemu-i-poluchi</guid>
      <description><![CDATA[<p>Mintlify построила ChromaFs — виртуальную ФС поверх Chroma DB, заменив RAG. Результат: 46 сек → 100 мс, нулевая стоимость, 30 000+ диалогов в день. Разбираем архитектуру.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/mintlify-zamenila-rag-na-virtualnuyu-fajlovuyu-sistemu-i-poluchi">Mintlify заменила RAG на виртуальную файловую систему — и получила ускорение в 460 раз</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 07 Apr 2026 11:47:02 GMT</pubDate>
      <content:encoded><![CDATA[<p><b>Если вы строите ИИ-ассистент поверх документации — возможно, RAG вам не нужен.</b> Команда <a href="https://www.mintlify.com/blog/how-we-built-a-virtual-filesystem-for-our-assistant">Mintlify</a> заменила RAG на виртуальную файловую систему и получила: загрузку сессии за 100 мс вместо 46 секунд (ускорение в 460 раз), нулевую стоимость вычислений и 30 000+ диалогов в день.</p><p>RAG (Retrieval-Augmented Generation) — стандартный подход к построению ИИ-ассистентов: нарезать документацию на чанки, сохранить эмбеддинги в векторную базу, искать top-K релевантных фрагментов по запросу. Но у него есть фундаментальное ограничение: агент видит только те фрагменты, которые совпали с запросом. Если ответ распределён по нескольким страницам или пользователю нужен точный синтаксис — RAG буксует.</p><ul><li>Mintlify заменила RAG на ChromaFs — виртуальную ФС поверх Chroma DB</li><li>Время создания сессии: 46 сек → 100 мс (460x быстрее)</li><li>Стоимость на диалог: $0,0137 → $0 (используется существующая инфраструктура)</li><li>Агент работает через grep, cat, ls, find — как с обычной файловой системой</li><li>Встроенный RBAC: контроль доступа без Linux-прав и chmod</li><li>30 000+ диалогов в день на существующей инфраструктуре</li></ul><h2>Проблема: песочницы медленные и дорогие</h2><p>Очевидный способ дать агенту файловую систему — развернуть контейнер и склонировать репозиторий. Mintlify <a href="https://www.mintlify.com/blog/how-we-built-a-virtual-filesystem-for-our-assistant">так и делала</a> для фоновых задач, но для фронтенд-ассистента, где пользователь смотрит на спиннер, подход не работал:</p><ul><li><b>P90 время создания сессии:</b> ~46 секунд (включая клонирование с GitHub)</li><li><b>Стоимость:</b> при 850 000 диалогов в месяц даже минимальная конфигурация (1 vCPU, 2 ГБ RAM, 5-минутный TTL) обошлась бы в $70 000+/год</li><li>Каждому пользователю нужен отдельный контейнер, иначе агенты видят чужие данные</li></ul><h2>Решение: ChromaFs — виртуальная ФС поверх Chroma</h2><p>Агенту не нужна настоящая файловая система — ему нужна <b>иллюзия</b> файловой системы. Документация Mintlify уже была индексирована и хранилась в Chroma DB для поиска. Команда построила <b>ChromaFs</b> — виртуальную ФС, которая перехватывает UNIX-команды и транслирует их в запросы к Chroma.</p><p>ChromaFs построена на <a href="https://www.mintlify.com/blog/how-we-built-a-virtual-filesystem-for-our-assistant">just-bash</a> от Vercel Labs — TypeScript-реализации bash, которая поддерживает grep, cat, ls, find и cd. just-bash предоставляет интерфейс IFileSystem — ChromaFs реализует его, транслируя каждый вызов файловой системы в запрос к Chroma.</p><h2>Как это работает</h2><h3>Дерево файлов из базы данных</h3><p>При инициализации ChromaFs загружает сжатый JSON-документ с деревом путей из Chroma-коллекции и разворачивает его в две структуры в памяти: множество путей файлов и маппинг директорий на дочерние элементы. После этого ls, cd и find работают из памяти без сетевых запросов.</p><h3>Сборка страниц из чанков</h3><p>Страницы в Chroma разбиты на чанки для эмбеддингов. Когда агент выполняет cat /auth/oauth.mdx, ChromaFs находит все чанки с подходящим slug, сортирует по индексу и склеивает в полную страницу. Результаты кешируются — повторные чтения не обращаются к базе.</p><h3>Оптимизация grep</h3><p>Рекурсивный grep -r по всем файлам через сеть был бы слишком медленным. ChromaFs перехватывает команду, парсит флаги и транслирует поиск в запрос к Chroma ($contains для фиксированных строк, $regex для паттернов). Chroma выступает грубым фильтром — определяет, в каких файлах может быть совпадение. Эти чанки загружаются в Redis-кеш, и уже из него just-bash выполняет точный поиск в памяти.</p><h3>Контроль доступа (RBAC)</h3><p>Каждый путь в дереве файлов имеет флаги доступа: isPublic и groups. Перед построением дерева ChromaFs фильтрует пути по токену сессии пользователя. Если у пользователя нет доступа к файлу — файл полностью исключается из дерева. Агент не может даже узнать о его существовании.</p><p>В настоящей песочнице такой контроль потребовал бы управления Linux-группами, chmod-правами или отдельных контейнеров для каждого тарифа. В ChromaFs — это несколько строк фильтрации.</p><h2>Результаты</h2><ul><li><b>P90 время создания сессии:</b> ~46 секунд → ~100 мс (460x быстрее)</li><li><b>Стоимость на диалог:</b> ~$0,0137 → ~$0</li><li><b>Масштаб:</b> 30 000+ диалогов в день на существующей инфраструктуре</li><li><b>Запись заблокирована:</b> все операции записи возвращают EROFS — агент не может модифицировать документацию, система полностью stateless</li></ul><h2>Когда это работает, а когда нет</h2><p>Подход Mintlify работает для <b>структурированной документации</b>, где каждая страница — файл, а каждый раздел — директория. Агент использует привычные UNIX-примитивы (grep, cat, find), и ему не нужно учиться новому API.</p><p>Ограничения:</p><ul><li>Нужна уже индексированная и чанкованная документация в векторной базе</li><li>Не подходит для задач, где агенту нужно <b>модифицировать</b> файлы (виртуальная ФС read-only)</li><li>Для неструктурированных данных (чаты, тикеты, логи) RAG может быть эффективнее</li><li>Зависимость от качества чанкирования — плохое разбиение = плохие результаты cat</li></ul><h2>Выводы</h2><p>Подход Mintlify показывает важный тренд: для структурированных данных агентам не нужны сложные RAG-пайплайны — достаточно привычного интерфейса файловой системы. Когда ваши данные уже в базе, виртуальная ФС может быть быстрее, дешевле и понятнее для агента, чем векторный поиск. Особенно если агент уже умеет работать с grep и cat.</p><p>Источник: <a href="https://www.mintlify.com/blog/how-we-built-a-virtual-filesystem-for-our-assistant">Mintlify — How we built a virtual filesystem for our Assistant</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Caveman: плагин для Claude Code, который экономит 75% токенов через «пещерный» стиль</title>
      <link>https://tproger.ru/articles/caveman--plagin-dlya-claude-code--kotoryj-ekonomit-75--tokenov-che</link>
      <comments>https://tproger.ru/articles/caveman--plagin-dlya-claude-code--kotoryj-ekonomit-75--tokenov-che?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/caveman--plagin-dlya-claude-code--kotoryj-ekonomit-75--tokenov-che</guid>
      <description><![CDATA[<p>Caveman — плагин для Claude Code и Codex, который заставляет ИИ отвечать максимально кратко. Экономия output-токенов до 87%, три уровня сжатия, научное обоснование.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/caveman--plagin-dlya-claude-code--kotoryj-ekonomit-75--tokenov-che">Caveman: плагин для Claude Code, который экономит 75% токенов через «пещерный» стиль</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 06 Apr 2026 10:58:55 GMT</pubDate>
      <content:encoded><![CDATA[<p>Каждый раз, когда ИИ-ассистент объясняет вам очевидное в трёх абзацах, вы платите за это токенами. Output-токены дороже input, а модели вроде Claude и GPT-4 любят добавлять вводные фразы, повторять вопрос и объяснять то, о чём вас не спрашивали. Разработчики, которые активно используют ИИ в работе, тратят на это сотни долларов в месяц.</p><p>Плагин <a href="https://github.com/juliusbrussee/caveman">Caveman</a> решает эту проблему радикально: он заставляет ИИ-ассистента отвечать как пещерный человек — только самое необходимое. Никакого «The reason your component is re-rendering is likely because...» — только «New object ref each render. Wrap in useMemo.» По данным автора, в среднем экономия составляет 75% output-токенов. На Hacker News проект набрал 797 очков, на GitHub — 2,8K звёзд за несколько дней.</p><ul><li>Caveman сокращает output-токены в среднем на 65–75%, не затрагивая «мышление» модели</li><li>Работает как плагин для Claude Code и OpenAI Codex через одну команду установки</li><li>Три уровня сжатия: Lite, Full и Ultra — под разные задачи</li><li>Научное исследование марта 2026 года подтвердило, что краткость ответов повышает точность моделей на 26 п.п.</li></ul><h2>До и после: как выглядят ответы</h2><p>Вот реальный пример ответа на вопрос о повторном рендеринге React-компонента. Обычный режим — 69 токенов:</p><p>Тот же вопрос с Caveman — 19 токенов:</p><p>Смысл тот же, шум убран. Девиз проекта: «Why use many token when few do trick.»</p><h2>Бенчмарки на реальных задачах</h2><p>Автор замерил расход токенов на десяти типичных задачах через Claude API. Результаты:</p><ul><li>Объяснить баг с повторным рендером React: 1180 → 159 токенов (−87%)</li><li>Исправить middleware для токена авторизации: 704 → 121 (−83%)</li><li>Настроить пул соединений PostgreSQL: 2347 → 380 (−84%)</li><li>Объяснить git rebase vs merge: 702 → 292 (−58%)</li><li>Рефакторинг callback → async/await: 387 → 301 (−22%)</li><li>Архитектура: микросервисы vs монолит: 446 → 310 (−30%)</li><li>Ревью PR на безопасность: 678 → 398 (−41%)</li><li>Docker multi-stage build: 1042 → 290 (−72%)</li><li>Отладка race condition в PostgreSQL: 1200 → 232 (−81%)</li><li>Реализовать React error boundary: 3454 → 456 (−87%)</li><li>Среднее: 1214 → 294 токена (−65%)</li></ul><p>Наибольший эффект там, где модель обычно «разогревается» перед ответом: объяснения, отладка, архитектурные обсуждения. На задачах с конкретным кодом (рефакторинг, ревью) выигрыш скромнее — модель и так пишет по делу.</p><h2>Три уровня сжатия</h2><p>Caveman предлагает три режима работы — в зависимости от задачи и допустимого уровня «дикости» ответов:</p><ul><li><b>Lite</b> — убирает вводные фразы и повторения, сохраняет связный текст. Подходит для объяснений и документации.</li><li><b>Full</b> — сокращает до ключевых утверждений, телеграфный стиль. Оптимален для отладки и код-ревью.</li><li><b>Ultra</b> — максимальная экономия, ответы из 2–5 слов на пункт. Для быстрых уточнений и одностроковых подсказок.</li></ul><h2>Наука подтверждает: меньше слов — лучше ответ</h2><p>В марте 2026 года вышла статья «<a href="https://arxiv.org/">Brevity Constraints Reverse Performance Hierarchies in Language Models</a>», в которой исследователи проверили, как ограничения длины ответа влияют на качество. Вывод неожиданный: принудительное сокращение ответов улучшило точность моделей на 26 процентных пунктов. Авторы объясняют это тем, что при жёстком лимите модель вынуждена сразу формулировать суть, а не «думать вслух» в ответе.</p><p>Важно понимать: Caveman влияет только на output-токены — то, что модель пишет вам в ответ. Thinking-токены (внутренние рассуждения Claude) плагин не трогает. Как говорит сам проект: «Caveman no make brain smaller. Caveman make mouth smaller.»</p><h2>Как установить</h2><p>Плагин устанавливается одной командой через npx skills:</p><p>После установки плагин подключается к Claude Code или Codex и начинает работать автоматически. Уровень сжатия можно переключать в настройках или через аргументы команды.</p><p>Caveman — редкий случай, когда оптимизация расходов не требует компромисса по качеству. Если вы платите за API Claude или GPT-4 и используете ИИ-ассистента для рутинных задач разработки, плагин окупится быстро. Средняя экономия 65% означает, что при счёте $100/месяц вы начнёте тратить $35. А пещерный стиль ответов — это просто побочный эффект, к которому привыкаешь за час работы.</p>]]></content:encoded>
    </item>
    <item>
      <title>Аппаратное сжатие текстур: AFRC, PVRIC4 и Metal</title>
      <link>https://tproger.ru/translations/apparatnoe-szhatie-tekstur--afrc--pvric4-i-metal</link>
      <comments>https://tproger.ru/translations/apparatnoe-szhatie-tekstur--afrc--pvric4-i-metal?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/apparatnoe-szhatie-tekstur--afrc--pvric4-i-metal</guid>
      <description><![CDATA[<p>Сравнение трёх форматов аппаратного сжатия текстур — ARM AFRC, ImgTec PVRIC4 и Apple Metal Lossy. Точные данные RMSE, производительность в МПикс/сек, Vulkan BPC.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/apparatnoe-szhatie-tekstur--afrc--pvric4-i-metal">Аппаратное сжатие текстур: AFRC, PVRIC4 и Metal</a>»</p>]]></description>
      <category><![CDATA[Низкоуровневое программирование]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Apple]]></category>
      <category><![CDATA[Hardware]]></category>
      <category><![CDATA[Компьютерная графика]]></category>
      <category><![CDATA[ARM]]></category>
      <category><![CDATA[Алгоритмы сжатия]]></category>
      <category><![CDATA[GPU]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 30 Mar 2026 15:22:10 GMT</pubDate>
      <content:encoded><![CDATA[<p>Перевод статьи <a href="https://www.ludicon.com/castano/blog/2026/03/hardware-image-compression/">Hardware Image Compression</a> Игнасио Кастаньо (Ignacio Castaño). Оригинал опубликован в марте 2026 года.</p><p>Одним из поводов для разочарования в области аппаратных форматов изображений всегда было медленное развитие. Разработчики обычно не решались поставлять текстуры в новом формате, пока он не становился повсеместно доступным — поддерживался большинством целевого железа и всеми вендорами без исключения. Сегодня три вендора предлагают собственные форматы аппаратного сжатия текстур на лету: ARM AFRC, ImgTec PVRIC4 и Apple Metal Lossy. Игнасио Кастаньо исследовал все три и сравнил их с программным сжатием реального времени Spark от NVIDIA.</p><p>— ARM AFRC (Pixel 8, Mali-G715) — явный победитель: по метрике RMSE значительно обходит Spark и других конкурентов во всех форматах.</p><p>— Apple Metal Lossy (A15/M2+) — коэффициент сжатия 1:2, API минималистичен: один флаг в дескрипторе текстуры.</p><p>— ImgTec PVRIC4 (Pixel 10) разочаровал: драйвер игнорирует запрошенный bitrate, качество хуже Spark для R и RG форматов.</p><p>— Vulkan-расширение VK_EXT_image_compression_control унифицирует доступ к аппаратному сжатию через параметр BPC (bits per component).</p><p>— Аппаратное сжатие — перспективная альтернатива программному, но пока ограничено современными топовыми устройствами.</p><h2>Контекст: почему это важно</h2><p>Разработчики обычно не решались поставлять текстуры в новом формате, пока он не становился широко доступным — то есть поддерживался большинством целевого железа и всеми вендорами. Например, хотя ATI представила форматы 3Dc в 2004 году вместе с Radeon X800 (R420) и открыла их через расширения D3D9, их использование не стало распространённым, когда Direct3D 10 стандартизировал их как BC4 и BC5 в 2007 году. Массовое применение началось лишь тогда, когда Direct3D 10 стал минимальным требованием к железу.</p><p><a href="https://www.crytek.com/games/crysis">Crysis</a> стала первой крупной игрой, поставляемой с BC5-текстурами, но большинство игр ещё долгие годы не решались устанавливать такое жёсткое требование к железу. Чтобы избежать задержек с принятием, форматы BC6 и BC7 разрабатывались совместно ATI и NVIDIA для Direct3D 11.</p><p>Именно поэтому сжатие текстур в реальном времени так интересно: когда кодировщик работает в реальном времени, внедрять новые аппаратные форматы значительно проще — не нужно ждать, пока будет подготовлен контент, целенаправленно созданный под них. Аппаратное сжатие устраняет эту проблему принятия: детали форматов не документируются, их использование абсолютно прозрачно — приложению не нужно явно указывать эти форматы, драйвер сжимает текстуры динамически в процессе рендеринга и загрузки изображений.</p><h2>Apple Metal: Lossy-сжатие</h2><p>Apple представила сжатие текстур с потерями в чипах A15 и M2 (оба используют одно поколение GPU). Оно обеспечивает коэффициент сжатия 1:2. Включить его на удивление просто — API минималистичен. Свойство compressionType дескриптора MTLTextureDescriptor принимает значение из перечисления MTLTextureCompressionType, и установка MTLTextureCompressionTypeLossy зачастую является единственным необходимым изменением:</p><p>Согласно таблицам возможностей Metal Feature Set Tables, все обычные пиксельные форматы поддерживают сжатие с потерями — включая 10-битные и форматы с плавающей точкой. Тестирование это подтвердило, однако основное внимание сосредоточено на форматах R8, RG8 и RGBA8.</p><p>Внутренний алгоритм Apple не задокументирован. По результатам реверс-инжиниринга lossy-форматов используется размер блока 8×4 пикселя — и они напоминают некоторые особенности форматов ETC и EAC. Несмотря на заявленное сжатие 1:2, на практике для каждого блока выделяется один байт метаданных, так что реальное потребление памяти чуть выше заявленного.</p><h3>Качество на M4 Pro</h3><p>Для форматов R и RG Metal Lossy показывает результаты лучше кодеков Spark EAC, но хуже BC4 и BC5. Результаты RMSE (Root Mean Square Error — среднеквадратичная ошибка; меньше = лучше):</p><p><b>Формат R:</b><br /><br /></p>МетрикаMetal Lossy (1:2)BC4 Medium (1:2)BC4 High (1:2)EAC_RG Low (1:2)EAC_RG Medium (1:2)EAC_RG High (1:2)RMSE1,85791,84691,71492,33992,29221,8636<p></p><p><b>Формат RG:</b><br /><br /></p>МетрикаMetal Lossy (1:2)BC5 Medium (1:2)BC5 High (1:2)EAC_RG Low (1:2)EAC_RG Medium (1:2)EAC_RG High (1:2)RMSE3,17573,30993,04424,22614,15923,3601<p></p><p>Прямое сравнение lossy RGBA8 с форматами Spark некорректно из-за разных коэффициентов сжатия: Metal Lossy поддерживает только 1:2, тогда как форматы Spark RGB(A) — 1:4. Тем не менее, для полноты картины:</p><p><b>Формат RGBA:</b><br /><br /></p>МетрикаMetal Lossy (1:2)ASTC 4×4 Low (1:4)ASTC 4×4 Medium (1:4)ASTC 4×4 High (1:4)BC7 Low (1:4)BC7 Medium (1:4)BC7 High (1:4)RMSE1,49476,29945,96865,36375,72135,35854,2136<p></p><h3>Производительность на M4 Pro</h3><p>С точки зрения производительности lossy-форматы показывают себя отлично и при достаточно большом размере текстуры упираются в пропускную способность памяти. Результаты на M4 Pro (16 ядер GPU) в МПикс/сек:</p><p></p>Метод409620481024512256Uncompressed (blit)41 61826 68043 74970 11144 939Metal Lossy (blit)41 80740 84743 10069 87348 729BC7 High (GPU)35 56342 23037 08234 22410 985<p></p><p>Обратите внимание, что пропускная способность стандартных блитов остаётся достаточно стабильной вне зависимости от размера текстуры. Кодеки Spark, напротив, имеют фиксированные накладные расходы, которые становятся заметнее при уменьшении размеров текстур. Интересен скачок скорости блитов при 512×512 — у него пока нет объяснения. Также стоит учесть, что кодеки Spark требуют дополнительного копирования из буфера вывода кодека в финальную сжатую текстуру — это дополнительные накладные расходы, которых можно было бы избежать, если бы Metal поддерживал запись в блочно-сжатые текстуры, как это делает Vulkan.</p><h2>Vulkan: расширение VK_EXT_image_compression_control</h2><p>В Vulkan расширение VK_EXT_image_compression_control даёт приложениям возможность запрашивать сжатие изображений с фиксированной скоростью. Расширение уже доступно на флагманских устройствах от ARM и Imagination. Включение потерявого сжатия в Vulkan несколько многословнее, чем в Metal, но на практике ненамного сложнее: нужно лишь расширить структуру VkImageCreateInfo, добавив в цепочку VkImageCompressionControlEXT.</p><p>Можно использовать флаг VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT, чтобы позволить реализации самой выбрать параметры сжатия:</p><p>Альтернативно можно явно указать флаги фиксированной скорости для управления допустимыми коэффициентами сжатия:</p><p>BPC (bits per component — бит на компонент) — несколько нестандартная единица, но она позволяет задавать коэффициент сжатия единообразно вне зависимости от количества каналов. Для справки, BPC существующих форматов блочного сжатия GPU:</p><p></p>ФорматКаналыРазмер на пиксельРазмер на каналBC1RGB4 bpp~1,33 bpcBC4R4 bpp4 bpcBC5RG8 bpp4 bpcBC7RGBA8 bpp2 bpcASTC 4×4RGBA8 bpp2 bpcASTC 6×6RGBA~3,55 bpp~1,18 bpc<p></p><p>Расширение VK_EXT_image_compression_control также присутствует в некоторых драйверах AMD и Qualcomm, однако, насколько известно, ни один из этих вендоров не поддерживает аппаратное сжатие изображений с фиксированной скоростью. В случае AMD расширение присутствует в драйвере RADV, чтобы Proton мог отключать потерявое сжатие фреймбуфера в некоторых играх, где оно вызывало проблемы с корректностью — через флаг VK_IMAGE_COMPRESSION_DISABLED_EXT.</p><h2>ARM AFRC: лучший результат среди всех</h2><p>ARM Fixed Rate Compression (AFRC) было анонсировано в 2021 году и впервые появилось в Mali-G510 в 2022-м, однако широкого распространения этот дизайн не получил. По-настоящему массовым AFRC стало с выпуском Mali-G715 и Mali-G615 в том же году. Тестирование проводилось на Pixel 8 с GPU Mali-G715.</p><p>Устройство сообщило поддержку следующих форматов сжатия с фиксированной скоростью (возможности значительно шире Metal Lossy):</p><p></p>Формат2 bpc3 bpc4 bpc5 bpcR82 bpp3 bpp4 bpp—RG84 bpp6 bpp8 bpp—RGB86 bpp—12 bpp15 bppRGBA88 bpp12 bpp16 bpp—<p></p><p>В отличие от Metal Lossy, AFRC не использует дополнительных байт метаданных — все управляющие биты находятся внутри самого блока. Изображение делится на блоки 8×8 пикселей, в ряде случаев разбиваемые на подблоки меньшего размера. Размер каждого блока 8×8 в байтах:</p><p></p>Формат2 bpc3 bpc4 bpc5 bpcR8162432—RG8324864—RGB864—96128RGBA86496128—<p></p><p>По результатам реверс-инжиниринга удалось установить: AFRC представляет цвета с использованием преобразования YCoCg, а представление пикселей напоминает вейвлет Хаара. Для каждого подблока 4×4 используется 16 коэффициентов, квантование которых зависит от режима. Форматы RGB и RGBA принципиально одинаковы — флаг в заголовке просто указывает, присутствует ли альфа-канал или блок полностью непрозрачен.</p><h3>Качество AFRC</h3><p>Качество AFRC впечатляет. В отличие от Metal, здесь можно сделать прямое сравнение с ASTC в реальном времени, поскольку Mali поддерживает сжатие 1:4:</p><p><b>Формат R:</b><br /><br /></p>МетрикаAFRC (1:2)EAC_R Low (1:2)EAC_R Medium (1:2)EAC_R High (1:2)RMSE<b>1,4937</b>2,33992,29221,8636<p></p><p><b>Формат RG:</b><br /><br /></p>МетрикаAFRC (1:2)EAC_RG Low (1:2)EAC_RG Medium (1:2)EAC_RG High (1:2)RMSE<b>2,2079</b>4,22614,15923,3601<p></p><p><b>Формат RGBA:</b><br /><br /></p>МетрикаAFRC (1:2)AFRC (1:4)ASTC 4×4 Low (1:4)ASTC 4×4 Medium (1:4)ASTC 4×4 High (1:4)RMSE<b>0,6679</b><b>3,4184</b>6,29945,96865,3637<p></p><p>Во всех случаях RMSE значительно ниже, то есть AFRC превосходит Spark ASTC при нацеливании на ASTC с кодировщиком реального времени. Тем не менее есть несколько случаев, когда Spark даёт более высокое качество: на очень гладких изображениях сжатие AFRC приводит к заметным паттернам дизеринга, раскрывающим границы блоков. Это особенно заметно при использовании AFRC в качестве текстуры с увеличением, тогда как для сжатия фреймбуфера — основного сценария применения — такие паттерны практически незаметны.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-30/7b1601e9-42b2-4416-a0d0-dfda1bf1cafa.webp" alt="Сравнение AFRC с дизерингом и без: AFRC сохраняет детали значительно лучше конкурентов при том же bitrate" /><figcaption>Сравнение AFRC с дизерингом и без: AFRC сохраняет детали значительно лучше конкурентов при том же bitrate</figcaption></figure><h3>Производительность AFRC (Pixel 8)</h3><p>Включение AFRC не вызывает значительных накладных расходов по сравнению с несжатыми загрузками текстур, за исключением некоторых размеров. Результаты на Pixel 8 в МПикс/сек:</p><p></p>Метод409620481024512256Uncompressed4 9613 9513 0632 2902 337AFRC 4 bpc5 5083 7921 7712 3412 318AFRC 2 bpc5 0414 4332 5562 2672 332Spark ASTC Q04 8104 2072 5033 6622 259Spark ASTC Q24 4813 7152 3192 9501 903<p></p><p>Пропускная способность здесь масштабируется с размером текстуры, а не остаётся стабильной. Накладные расходы одинаково влияют на блиты и compute-шейдеры Spark. В отличие от секции Metal, где lossy-блиты явно доминировали над Spark на малых размерах, здесь картина смешанная: Spark вплотную приближается к AFRC или превосходит его. Это показывает, что кодирование текстур в реальном времени вполне конкурентоспособно с аппаратным сжатием. Абсолютные числа здесь значительно ниже, чем на M4 Pro, — это принципиально разные классы устройств.</p><h2>ImgTec PVRIC4: разочарование от Pixel 10</h2><p>ImgTec впервые анонсировала поддержку PVRIC4 ещё в 2018 году для GPU серии Series 6, однако протестировать её удалось только с выходом Pixel 10 на чипе Series D. Первоначальное объявление намекало на то, что, как и Metal Lossy, PVRIC4 поддерживает только 50%-е сжатие, но расширение декларирует более широкий спектр опций:</p><p></p>Формат1 bpc2 bpc3 bpc4 bpcR81 bpp2 bpp3 bpp4 bppRG82 bpp4 bpp6 bpp8 bppRGBA84 bpp8 bpp12 bpp16 bpp<p></p><p>К большому удивлению, качество вывода оказалось одинаковым вне зависимости от BPC. Дальнейшее расследование показало: <b>драйвер игнорирует запрошенный BPC и всегда использует 4 bpc (сжатие 1:2)</b>. Формат блоков PVRIC4 — наиболее сложный из всех трёх вендоров: размер блоков составляет 16×16 пикселей, и, как в Metal Lossy, присутствует один байт метаданных на блок. Сделать реверс-инжиниринг практически не удалось.</p><h3>Качество PVRIC4</h3><p>Качество оказалось разочаровывающим. Для форматов R и RG Spark фактически превосходит PVRIC4 при нацеливании на стандартные форматы блочного сжатия, поддерживаемые этим железом:</p><p><b>Формат R:</b><br /><br /></p>МетрикаPVRIC4 (1:2)BC4 Medium (1:2)BC4 High (1:2)EAC_R Low (1:2)EAC_R Medium (1:2)EAC_R High (1:2)RMSE3,43461,84691,71492,33992,29221,8636<p></p><p><b>Формат RG:</b><br /><br /></p>МетрикаPVRIC4 (1:2)BC5 Medium (1:2)BC5 High (1:2)EAC_RG Low (1:2)EAC_RG Medium (1:2)EAC_RG High (1:2)RMSE5,43923,30993,04424,22614,15923,3601<p></p><p>Для RGBA прямое сравнение невозможно из-за разных коэффициентов сжатия, но качество также значительно хуже других вендоров:</p><p><b>Формат RGBA:</b><br /><br /></p>МетрикаPVRIC4 (1:2)ASTC 4×4 Low (1:4)ASTC 4×4 Medium (1:4)ASTC 4×4 High (1:4)RMSE2,31606,29945,96865,3637<p></p><h3>Производительность PVRIC4 (Pixel 10)</h3><p></p>Метод409620481024512256Uncompressed2 2992 6292 6431 9091 178PVRIC4 4 bpc2 5822 9723 8512 8771 102Spark ASTC Q03 3273 5093 0972 051911Spark ASTC Q23 0022 7592 4981 485634<p></p><p>Кривая пропускной способности на этом устройстве существенно отличается от Pixel 8: пик приходится на размеры 1024–2048, а не монотонно возрастает с размером. На больших размерах пропускная способность Spark фактически выше, чем у несжатых загрузок текстур. Это характерно для устройств, ограниченных пропускной способностью памяти: обычный блит должен прочитать всё входное изображение и записать обратно тот же объём данных, тогда как Spark записывает лишь 1/4 входных данных. Экономия на записи зачастую перекрывает вычислительные затраты кодирования.</p><p><b>Комментарий PowerVR Dev (ImgTec)</b><br />PVRIC4 поддерживает только сжатие 1:2. TFBC (доступен исключительно в чипах Rogue XE) поддерживал соотношения 1:2, 1:4 и 1:3. Дизайн расширения был спорным — многим не нравилась многословность выбора BPC, поэтому было решено экспонировать все BPC в рамках поддерживаемых соотношений и повышать их до поддерживаемого ratio. Это считалось допустимым, поскольку качество не деградирует относительно запроса приложения, а приложение всё равно обязано запрашивать реальный размер изображения, который не гарантирует уменьшения.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-30/34deaeec-ec94-4d39-9a82-dd707dcf71a6.webp" alt="Сравнение Spark ASTC с дизерингом и без. Программный Spark показывает хорошие результаты, но AFRC его превосходит" /><figcaption>Сравнение Spark ASTC с дизерингом и без. Программный Spark показывает хорошие результаты, но AFRC его превосходит</figcaption></figure><h2>Spark: программная точка отсчёта</h2><p><a href="https://github.com/NVIDIA/Spark">Spark</a> — библиотека NVIDIA для программного сжатия текстур в реальном времени. Работает на GPU как compute shader, поддерживает форматы BC1–BC7 и ASTC. В тесте выступает базовой точкой сравнения как лучшее доступное программное сжатие без специализированного железа.</p><p>Spark показывает стабильные результаты по форматам, но требует явного вызова compute-прохода — это дополнительный этап в пайплайне. Аппаратное сжатие, напротив, встроено в рендеринг и не требует дополнительного кода. Отдельный плюс Spark — предсказуемое и согласованное поведение на всех устройствах, что важно, если унифицированный вывод критичен для вашего сценария. Ни один из форматов аппаратного сжатия пока не доступен через WebGPU — если это изменится, расширить spark.js для их поддержки будет несложно.</p><h2>Итоги: кто победил</h2><p>ARM AFRC — явный победитель. Это не только превосходит программные реализации вроде Spark, но и обходит все остальные форматы по всем метрикам. Итоговая таблица RMSE по всем протестированным форматам:</p><p></p>ФорматRMSE 1:2RMSE 1:4R8 Metal Lossy1,8579—<b>R8 AFRC</b><b>1,4937</b>—R8 PVRIC43,4346—Spark BC41,7149—RG8 Metal Lossy3,1757—<b>RG8 AFRC</b><b>2,2079</b>—RG8 PVRIC45,4392—Spark BC53,0442—RGBA8 Metal Lossy1,4947—<b>RGBA8 AFRC</b><b>0,6679</b><b>3,4184</b>RGBA8 PVRIC42,3160—Spark BC7—4,2136<p></p><p>Стоит оговориться: результаты PVRIC4 могут не отражать полного потенциала железа — драйвер игнорирует запрошенный коэффициент сжатия и всегда использует 1:2. Возможно, эти результаты удастся пересмотреть после исправления проблемы.</p><p>Аппаратное сжатие — убедительная альтернатива программному. Главная оговорка — оно сейчас ограничено современными топовыми устройствами, которые как раз и располагают наибольшим объёмом памяти и пропускной способностью.</p><p>Даже когда нативное аппаратное сжатие доступно, есть веские причины продолжать использовать Spark. Вывод аппаратного сжатия различается у разных вендоров, и в некоторых случаях — как мы видели с PVRIC4 — качество уступает кодировщику реального времени. Если для вашего сценария критичен единообразный и предсказуемый вывод на всех вендорах, Spark остаётся правильным выбором.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как случайно написать самый быстрый CSV-парсер на C#</title>
      <link>https://tproger.ru/translations/kak-sluchajno-napisat-samyj-bystryj-csv-parser-na-c-</link>
      <comments>https://tproger.ru/translations/kak-sluchajno-napisat-samyj-bystryj-csv-parser-na-c-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/kak-sluchajno-napisat-samyj-bystryj-csv-parser-na-c-</guid>
      <description><![CDATA[<p>Как SIMD-инструкции SSE2 и AVX2 ускоряют парсинг CSV в 10 раз. Разбираем путь от наивной реализации до библиотеки, обогнавшей Sep в 41 из 70 бенчмарков.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/kak-sluchajno-napisat-samyj-bystryj-csv-parser-na-c-">Как случайно написать самый быстрый CSV-парсер на C#</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[C#]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 15:28:21 GMT</pubDate>
      <content:encoded><![CDATA[<p>На рождественских каникулах 2025 года автор библиотеки <a href="https://github.com/bbepis/FourLambda.Csv">FourLambda.Csv</a> ехал в автобусе и читал про UTF-8. Обнаружив, что все ASCII-символы в UTF-8 занимают ровно один байт, он начал экспериментировать с подсчётом символов на максимальной скорости. Через несколько дней у него был готовый CSV-парсер на C#, который обогнал все существующие библиотеки в большинстве бенчмарков. Вот как это произошло.</p><blockquote><b>Ключевые выводы</b><br />- SIMD-инструкции (SSE2, AVX2) позволяют обрабатывать 16-32 байта за одну операцию<br />- В UTF-8 все управляющие символы CSV (запятая, кавычка, перенос строки) занимают ровно один байт и совпадают с ASCII<br />- Минималистичный код (16 КБ) побеждает оптимизированный, но раздутый (163 КБ) за счёт давления на L1-кэш процессора<br />- Компилятор и рантайм умеют оптимизировать лучше, если им не мешать микрооптимизациями</blockquote><h2>Наивная реализация — считаем запятые</h2><p>Прежде чем строить парсер, поставим простую задачу: как быстрее всего подсчитать, сколько раз символ , встречается в большом UTF-8-файле? Тестируем на CSV-файле размером 500 МБ.</p><h3>Базовая реализация: 135,305 мс</h3><p>Простейший вариант: перебираем каждый байт и сравниваем с искомым. Можно ли лучше?</p><h3>Unsafe-указатели: 128,683 мс</h3><p>Каждый раз, когда вы обращаетесь к массиву через data[i], рантайм .NET выполняет проверку границ (bounds checking) — убеждается, что индекс не выходит за пределы массива. Если вместо этого использовать unsafe-указатели, мы обещаем рантайму, что не выйдем за границы. Экономия — 7 мс.</p><h3>Развёртка цикла (unrolling 4x): 105,320 мс</h3><p>Механика цикла for обманчиво дорога: на каждой итерации нужно проверить условие выхода и выполнить переход в начало. Если обрабатывать по 4 элемента за итерацию, накладные расходы снижаются в четыре раза. «Хвост» данных, не кратный четырём, обрабатывается обычным побайтовым циклом.</p><h2>Проблема — UTF-8 и Unicode ломают всё</h2><p>Чтобы понять, почему мы можем сканировать текст <i>настолько</i> быстро, нужно разобраться, как текст хранится в памяти компьютера.</p><p>Наименьшая единица данных — байт (числа 0-255). Но человечество использует гораздо больше 256 символов. Ранние системы решали это через кодовые страницы — у каждого региона была своя таблица. Японские системы использовали Shift-JIS, в Китае по закону обязателен GB 18030. Если открыть файл в неправильной кодировке, вы увидите «крокозябры» (mojibake) — бессмысленный набор символов.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/1dd24e6b-e5ac-4bc8-986c-9c0566debde9.webp" alt="Пример mojibake — крокозябры при открытии файла в неправильной кодировке" /><figcaption>Так выглядит mojibake — результат открытия файла в неправильной кодировке</figcaption></figure><p>Unicode решил эту проблему в 90-х: каждому символу присваивается номер (codepoint), а разные кодировки (UTF-8, UTF-16, UTF-32) определяют, как эти номера записываются в байты.</p><p>Вот как UTF-8 кодирует символы в зависимости от количества байтов:</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/09bc69c0-ede8-4534-a016-3d01c7f7357f.webp" alt="Таблица ASCII — первые 128 символов, которые в UTF-8 кодируются одним байтом" /><figcaption>Таблица ASCII-символов: все они кодируются в UTF-8 ровно одним байтом</figcaption></figure><p>Ключевой факт: байты 0x00-0x7F в UTF-8 <b>полностью совпадают</b> с ASCII. Это значит, что управляющие символы CSV — запятая (,), кавычка ("), перевод строки (\n) — всегда занимают ровно один байт, независимо от того, какие Unicode-символы находятся в данных. И это можно использовать для невероятно быстрого сканирования.</p><h2>SIMD — ускорение через параллельные инструкции</h2><p>До сих пор мы обрабатывали по одному байту за раз. Представьте сложение двух массивов по 8 элементов — нужно 8 отдельных операций сложения. Можно распараллелить это по потокам, но управление потоками создаёт накладные расходы и не добавляет эффективности.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/a70a8c10-6a79-472a-b254-0c372fe72b71.webp" alt="Скалярное сложение — один элемент за раз" /><figcaption>Скалярная операция: процессор обрабатывает по одному элементу</figcaption></figure><p><b>SIMD</b> (Single Instruction, Multiple Data) — это набор процессорных инструкций, которые выполняют одну операцию сразу над несколькими элементами данных. Вся магия происходит в железе, поэтому результат приходит за то же время, что и обычная операция над одним элементом.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/1e1ba95a-bfc8-4abe-9db0-6a1d8fb8380a.webp" alt="SIMD — параллельная обработка нескольких элементов" /><figcaption>SIMD: четыре операции сложения за одну инструкцию</figcaption></figure><p>Аналогия: сложить два однозначных числа (8 + 2) — просто. Сложить два семизначных — нужно перебирать разряды. А теперь представьте, что вы можете «увидеть» сумму двух семизначных чисел мгновенно, с той же лёгкостью, что и однозначных. Это и есть SIMD.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/71aa521a-5fb7-457b-abdd-e169a3fba9ae.webp" alt="Широкий SIMD-регистр обрабатывает ещё больше данных" /><figcaption>Более широкие регистры позволяют обрабатывать больше элементов за такт</figcaption></figure><p>Вот как выглядит SIMD-сложение 4 элементов за одну операцию:</p><p>Мы делаем в 4 раза больше работы за то же усилие.</p><h3>SSE2: 46,663 мс</h3><p>SSE2 (появился в x86-процессорах в 2000 году) работает с 128-битными регистрами — 16 байтов за раз. Алгоритм такой:</p><ol><li>Загрузить 16 байтов данных в SIMD-вектор</li><li>Сравнить каждый байт с искомым (Sse2.CompareEqual) — на позициях совпадений появятся байты 255, на остальных — 0</li><li>Извлечь старшие биты из каждого байта (Sse2.MoveMask) — получаем 16-битное число, где каждый установленный бит означает совпадение</li><li>Подсчитать установленные биты</li></ol><p>Для строки "Hello, I, like, commas." это выглядит так:</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/64b6f6ff-d258-444a-9c06-9f382ab70ae9.webp" alt="Поиск разделителей через SIMD — CompareEqual и MoveMask" /><figcaption>SIMD-поиск запятых: сравнение вектора данных с вектором разделителей</figcaption></figure><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/6f22e456-df00-4a1e-aa15-3becd73f67b1.webp" alt="Расширенная SIMD-диаграмма для строки с Unicode" /><figcaption>Поиск разделителей в строке с Unicode-символами</figcaption></figure><h3>AVX2: 42,220 мс</h3><p>AVX2 (с 2011 года) расширяет регистры до 256 битов — 32 байта за итерацию. Код аналогичен SSE2, но с Vector256 вместо Vector128.</p><p>Прирост скромный — значит, узкое место сместилось на цикл обработки маски. Нужно ускорить подсчёт битов.</p><h3>AVX2 + TZCNT: 16,630 мс</h3><p>BitOperations.TrailingZeroCount — аппаратная инструкция TZCNT, которая мгновенно находит позицию младшего установленного бита. Вместо того чтобы перебирать биты маски по одному, мы прыгаем сразу к следующему совпадению и сбрасываем его.</p><h3>AVX2 + POPCNT: 13,673 мс</h3><p>А есть ещё лучше. BitOperations.PopCount — аппаратная инструкция POPCNT, которая за одну операцию считает количество установленных битов в числе. Именно то, что нам нужно:</p><p>Мы достигли потолка производительности для подсчёта символов. Ускорение в 10 раз по сравнению с наивной реализацией — отличный результат. Пора применить это к парсингу CSV.</p><h2>Поиск разделителей через SIMD</h2><p>При парсинге CSV нас интересуют три управляющих символа: запятая (,), кавычка (") и перевод строки (\n). Правила обработки:</p><ul><li>Запятая — конец текущего поля</li><li>Перевод строки — конец текущей строки</li><li>Кавычка — начало или конец экранированного блока (внутри которого запятые и переносы игнорируются). Две кавычки подряд — это экранированная кавычка в значении</li></ul><p>Парсинг разбивается на два этапа. Первый — определение позиций и длин полей через SIMD (функция DetermineFields). Второй — фактическое извлечение и дeкодирование значений, только когда пользователь их запрашивает.</p><p>Вот упрощённый код первого этапа (без обработки краевых случаев):</p><p>Несколько важных деталей:</p><ul><li>Здесь используется TrailingZeroCount вместо PopCount, потому что нам важна позиция каждого найденного символа, а не только их количество</li><li>Одна и та же схема CompareEqual + MoveMask применяется для каждого управляющего символа, а затем маски объединяются через OR. Отдельные маски сохраняются, чтобы потом определить тип найденного символа</li><li>Мы не касаемся содержимого полей — если пользователю нужен только один столбец, незачем декодировать остальные</li></ul><h2>Обработка кавычек и экранирования</h2><p>Когда поле содержит кавычки, нужно выполнить декодирование вручную — убрать обрамляющие кавычки и заменить двойные кавычки на одинарные. Кроме того, C# внутри использует UTF-16 для всех строк (char — это 16-битная единица кода), поэтому при чтении UTF-8 приходится конвертировать.</p><p>Если кавычек в поле нет, используется быстрая встроенная конвертация Encoding.UTF8.GetChars. Но если кавычки есть, быстрее выполнить конвертацию и удаление кавычек за один проход:</p><p>Хитрость здесь в том, что 2- и 3-байтовые UTF-8-последовательности всегда дают одну единицу кода UTF-16, поэтому кодпоинт можно просто привести к char. Четырёхбайтовые символы (эмодзи и редкие иероглифы) требуют суррогатных пар.</p><p>Для UTF-16-строк (внутренний формат C#) парсер использует трюк с Avx2.PackUnsignedSaturate: два 256-битных вектора 16-битных символов «сжимаются» в один 256-битный вектор 8-битных байтов. ASCII-символы сохраняются как есть, а все не-ASCII насыщаются до 255 (или 0 для отрицательных при знаковой интерпретации). После перестановки блоков через Avx2.Permute4x64 можно применять ту же маску, что и для UTF-8.</p><h2>Бенчмарки — результаты</h2><p>Существующие бенчмарки CSV-парсеров на C# тестируют только один файл — по сути, они измеряют не парсинг CSV, а производительность на одном конкретном сценарии. Поэтому автор создал собственный набор тестов с разными типами данных:</p><ul><li><b>Реальные данные Reddit</b> — широкий датасет с разными типами данных и большим количеством текста (из Reddit ArcticShift)</li><li><b>Международный текст</b> — CSV с названиями предметов из Pokemon на множестве языков (кириллица, японский, корейский)</li><li><b>Финансовые данные</b> — числа с минимумом текста</li><li><b>Синтетические тесты</b> — числовые поля, короткие файлы (50 строк), файлы с обязательным экранированием, «стресс-тест» с массой кавычек</li></ul><p>Полные результаты опубликованы на <a href="https://bepis.io/csv-benchmark/">bepis.io/csv-benchmark</a>.</p><p>Итог: <b>FourLambda.Csv оказался самым быстрым в 41 из 70 тестов</b>. Не абсолютная победа, но убедительное лидерство.</p><p>Для сравнения, вот как менялась скорость подсчёта символов на файле 500 МБ по мере оптимизации:</p><h3>Почему FourLambda.Csv быстрее Sep</h3><p>До этого самым быстрым CSV-парсером на C# считалась библиотека <a href="https://github.com/nietras/Sep">Sep</a>. Вот несколько причин, почему FourLambda.Csv выигрывает:</p><p><b>UTF-8 без промежуточной конвертации.</b> Sep внутри конвертирует UTF-8 в UTF-16 даже если данные не нужны пользователю. FourLambda.Csv работает напрямую с UTF-8.</p><p><b>Давление на кэш.</b> L1-кэш процессора — около 32-64 КБ, и он хранит не только данные, но и исполняемый код. FourLambda.Csv весит ~16 КБ, а Sep — ~163 КБ. Разница в 10 раз означает, что Sep может вытеснять данные из L1-кэша, вызывая медленные обращения к памяти.</p><p><b>Переоптимизация.</b> Sep тщательно контролирует, как данные перемещаются между регистрами. Теоретически это быстрее, но на практике может мешать оптимизациям рантайма. Компилятор и процессор умеют оптимизировать код, если им не указывают точный способ выполнения. Чрезмерная микрооптимизация «связывает руки» и мешает автоматическим улучшениям в новых версиях .NET.</p><p><b>Зависимость от оборудования.</b> Результаты могут отличаться на разных машинах. Например, Sse.Prefetch0 (предзагрузка данных в L1) давала +10-20% на ноутбуке с DDR3, но почти нулевой эффект на десктопе с DDR5. На системах с AVX-512 и ARM у Sep есть аппаратные оптимизации, которых пока нет в FourLambda.Csv.</p><h2>Выводы</h2><p>История этого проекта — хороший пример того, как глубокое понимание низкоуровневых деталей (кодировки, устройство кэша, SIMD-инструкции) может привести к результатам, которые превосходят годы целенаправленных оптимизаций в существующих библиотеках.</p><p>Главные уроки:</p><ul><li>SIMD — не магия для избранных. В C# интринсики доступны из коробки и дают кратный прирост на задачах поиска и сравнения</li><li>UTF-8-совместимость ASCII — мощное свойство, которое можно эксплуатировать для быстрого парсинга</li><li>Минимализм побеждает сложность: меньше кода — меньше давления на кэш — быстрее выполнение</li><li>Не мешайте компилятору: иногда лучшая оптимизация — не оптимизировать вручную</li></ul><p>Библиотека доступна на GitHub: <a href="https://github.com/bbepis/FourLambda.Csv">bbepis/FourLambda.Csv</a>.</p><p><i>Перевод и адаптация статьи <a href="https://bepis.io/blog/turbo-csv-parser/">How I accidentally made the fastest C# CSV parser</a> с разрешения автора.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Полнотекстовый поиск на Go без Elasticsearch — гайд по Bleve</title>
      <link>https://tproger.ru/translations/polnotekstovyj-poisk-na-go-bez-elasticsearch---gajd-po-bleve</link>
      <comments>https://tproger.ru/translations/polnotekstovyj-poisk-na-go-bez-elasticsearch---gajd-po-bleve?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/polnotekstovyj-poisk-na-go-bez-elasticsearch---gajd-po-bleve</guid>
      <description><![CDATA[<p>Bleve — встраиваемый полнотекстовый движок для Go. Разбираем кастомные маппинги, мультиязычные индексы, курсорную пагинацию и настройку Scorch для продакшена.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/polnotekstovyj-poisk-na-go-bez-elasticsearch---gajd-po-bleve">Полнотекстовый поиск на Go без Elasticsearch — гайд по Bleve</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 15:25:26 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда в проекте появляется задача полнотекстового поиска, первая мысль — поставить <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a> или <a href="https://www.meilisearch.com/">Meilisearch</a>. Оба инструмента отлично справляются. Но что, если вы не хотите зависеть от внешнего сервиса — или вам нужен полный контроль над хранением и обработкой данных?</p><p>В Go для этого есть <a href="https://blevesearch.com/">Bleve</a> — файловая библиотека полнотекстового индексирования. Она умеет индексировать любые Go-структуры с разумными настройками по умолчанию, поддерживает встроенный язык запросов в стиле Google, работает с миллионами записей и не требует отдельного сервера.</p><p>В этой статье — практический гайд: от простого индекса до кастомных анализаторов, мультиязычного поиска, пагинации курсором и тонкой настройки производительности.</p><blockquote><b>Ключевые выводы</b><br /><br />- Bleve — встраиваемый полнотекстовый движок для Go: не нужен отдельный сервер, достаточно одной зависимости<br />- Кастомные маппинги позволяют настроить анализатор, стемминг и стоп-слова для каждого поля отдельно<br />- IndexAlias объединяет несколько индексов (например, по языкам) в один виртуальный — с единым интерфейсом поиска<br />- Курсорная пагинация через SearchAfter / SearchBefore работает стабильно даже при обновлении индекса между запросами<br />- Scorch-настройки (воркеры, размер сегментов, порог мёржа) критичны для производительности под нагрузкой</blockquote><h2>Создаём простой индекс</h2><p>Начнём с минимального примера. Два базовых действия — <b>индексирование</b> (сохранение документа для последующего поиска) и <b>запрос</b> (извлечение документов, отсортированных по релевантности).</p><p>Несколько важных деталей:</p><ul><li><b>bleve.New vs bleve.Open</b> — New создаёт новый индекс по указанному пути, Open открывает существующий. Паттерн из примера (сначала New, при ошибке — Open) — идиоматический способ обработки первого и повторных запусков.</li><li><b>NewIndexMapping()</b> — возвращает маппинг по умолчанию: текстовые поля токенизируются, приводятся к нижнему регистру и фильтруются через список стоп-слов английского языка.</li><li><b>Автоматическое обнаружение полей</b> — Bleve использует рефлексию для анализа структуры. Все экспортируемые поля автоматически токенизируются и становятся доступными для поиска.</li><li><b>Уникальные ID документов</b> — строковый идентификатор, передаваемый в Index, используется для обновлений и удалений. Повторный вызов Index с тем же ID заменяет документ.</li><li><b>SearchRequest.Fields</b> — по умолчанию Bleve возвращает только ID и релевантность. Укажите имена нужных полей или []string{"*"}, чтобы получить все.</li><li><b>hit.Score</b> — каждый результат содержит числовой показатель релевантности на основе BM25. Чем выше — тем точнее совпадение.</li></ul><h2>Кастомные маппинги полей</h2><p>Маппинг по умолчанию подходит для быстрого старта, но в реальных проектах нужен контроль над тем, как Bleve анализирует и хранит каждое поле. <b>Маппинг</b> определяет тип поля, анализатор для токенизации, необходимость хранения оригинального значения и включение в индекс.</p><p>Маппинги позволяют:</p><ul><li><b>Управлять токенизацией</b> — разбивать текст на термы по пробелам, языковым правилам, edge n-граммам и другим стратегиям</li><li><b>Фильтровать входные данные</b> — приводить к нижнему регистру, удалять HTML, применять стоп-слова и стемминг (чтобы «running» и «runs» находились по запросу «run»)</li><li><b>Исключать поля из индекса</b> — пропускать чувствительные или нерелевантные данные для экономии дискового пространства</li><li><b>Создавать кастомные анализаторы</b> — комбинировать любой токенизатор с произвольной цепочкой фильтров</li></ul><p>Пример ниже применяет стемминг английского языка к полям Text и Title, а поле с сырым HTML исключает из индексирования:</p><p>Результат buildIndexMapping() передаётся в bleve.New или bleve.NewUsing при создании индекса. Маппинги закрепляются за индексом на этапе создания и не могут быть изменены позже. Чтобы применить новый маппинг, нужно создать свежий индекс и переиндексировать все документы.</p><h2>Мультиязычный поиск</h2><p>Bleve умеет прозрачно работать с несколькими индексами одновременно через <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#IndexAlias">IndexAlias</a>. Алиас — это виртуальный индекс, который отправляет запрос во все реальные индексы и объединяет результаты в единый ранжированный список.</p><p>Это особенно полезно, когда у каждого языка свой индекс с собственным анализатором (английский стемминг, французские стоп-слова, кастомная токенизация), а искать нужно по всем сразу:</p><p>Алиасы также упрощают горячую замену (hot-swap). Когда нужно перестроить индекс (например, применить новый маппинг), можно собрать новый индекс в фоне, а затем атомарно подменить его вызовом alias.Swap(newIndexes, oldIndexes). Текущие запросы завершатся на старом индексе, новые сразу пойдут на свежий — без даунтайма.</p><h2>Пагинация с курсором</h2><p>У SearchRequest в Bleve есть поле From для офсетной пагинации: 0 для первой страницы, 20 для второй и так далее. Это работает, но имеет серьёзную проблему: Bleve вынужден оценивать и сортировать <i>все</i> совпавшие документы вплоть до From + Size на каждый запрос. Глубокие страницы становятся всё дороже по памяти и CPU.</p><p>Хуже того, если между двумя запросами в индекс были добавлены новые документы, смещение сдвигается — и пользователь видит дубликаты или пропуски.</p><p>Правильный подход — курсорная пагинация через <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#SearchRequest.SetSearchAfter">SearchAfter</a> и <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#SearchRequest.SetSearchBefore">SearchBefore</a>. Эти методы продолжают выдачу с известной позиции, а не пересканируют результаты с начала:</p><p>На что обратить внимание:</p><ul><li><b>Стабильная сортировка обязательна.</b> SearchAfter использует ключ сортировки последнего результата в качестве курсора. Если ключ нестабилен, курсор станет невалидным.</li><li><b>Sort — всегда []string.</b> Даже при сортировке по числовому полю Bleve сериализует ключ в строку. Читайте курсор из hit.Sort и передавайте напрямую в SetSearchAfter.</li><li><b>SearchBefore работает аналогично,</b> но в обратном направлении — полезно для кнопки «предыдущая страница».</li></ul><h2>Оптимизация производительности</h2><p>Настройки производительности Bleve не слишком хорошо документированы, но существенно влияют на работу под нагрузкой. Конфигурация передаётся как map[string]any в <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#NewUsing">NewUsing</a> или <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#OpenUsing">OpenUsing</a> вместо обычных New / Open.</p><p>Эти параметры относятся к Scorch — дефолтному бэкенду хранения Bleve. Полный список доступных опций и значений по умолчанию можно найти в <a href="https://github.com/blevesearch/bleve/blob/master/index/scorch/persister.go#L67">исходном коде персистера</a>.</p><h3>Пакетная индексация</h3><p>Если нужно проиндексировать большой объём данных, используйте Batch вместо одиночных вызовов Index. Пакетная запись группирует операции в одну транзакцию, что значительно снижает нагрузку на диск:</p><p>Оптимальный размер пакета зависит от объёма документов и доступной памяти. Как правило, пакеты по 100-1000 документов дают хороший баланс между скоростью и потреблением ресурсов.</p><h2>Выводы</h2><p>Bleve — одна из недооценённых жемчужин экосистемы Go. Библиотека позволяет добавить полнотекстовый поиск в приложение без сложной инфраструктуры. Настройки по умолчанию дают рабочий результат за минуты, а кастомные маппинги, композитные запросы и тонкая настройка Scorch — инструменты для решения специфических задач оптимально.</p><p>Официальная документация местами неполна, но issues на GitHub и реальные open-source-проекты отлично её дополняют. Рабочий пример всех описанных концепций можно найти в <a href="https://github.com/asciimoo/hister/tree/master/server/indexer">пакете indexer</a> проекта Hister.</p><p><i>Адаптированный перевод статьи <a href="https://hister.org/posts/data-indexing-in-golang">Data Indexing in Golang</a> из блога Hister.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Пропуск 90% работы по деквантизации ускорил декодирование LLM на 22,8%</title>
      <link>https://tproger.ru/news/propusk-90--raboty-po-dekvantizacii-uskoril-dekodirovanie-llm-na</link>
      <comments>https://tproger.ru/news/propusk-90--raboty-po-dekvantizacii-uskoril-dekodirovanie-llm-na?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/propusk-90--raboty-po-dekvantizacii-uskoril-dekodirovanie-llm-na</guid>
      <description><![CDATA[<p>Разработчик ускорил декодирование LLM на 22,8% тремя строками кода: sparse V dequantization пропускает деквантизацию KV-кеша для позиций с малым весом внимания.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/propusk-90--raboty-po-dekvantizacii-uskoril-dekodirovanie-llm-na">Пропуск 90% работы по деквантизации ускорил декодирование LLM на 22,8%</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:53:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Три строчки кода в ядре flash attention — и декодирование большой языковой модели ускоряется на 22,8%. Без потери качества, без переобучения, без изменений в архитектуре модели.</p><p>Независимый разработчик <a href="https://github.com/TheTom">Tom Turney</a> опубликовал метод sparse V dequantization — оптимизацию для инференса LLM с квантизированным KV-кешем. Вместо того чтобы ускорять деквантизацию каждого значения, он предложил пропускать деквантизацию целиком для позиций, где вес внимания пренебрежимо мал. На Apple M5 Max с моделью Qwen 3.5 35B (MoE) метод дал прирост +22,8% к скорости декодирования на контексте 32K токенов. Перплексия осталась неизменной, а точность поиска needle-in-a-haystack выросла с 7/9 до 9/9.</p><blockquote><b>Ключевые выводы</b><br /><br />— Узкое место квантизированного KV-кеша — не то, <i>как</i> вы деквантизуете, а то, <i>сколько</i> значений вы деквантизуете.<br />— При длинном контексте (32K+) более 90% весов внимания пренебрежимо малы — их деквантизацию можно пропустить.<br />— Метод работает с любой схемой квантизации KV-кеша: TurboQuant, q8_0, q4_0, NVFP4.<br />— Реализация — 3 строки в ядре flash attention. Не нужно ни переобучение, ни калибровочные данные.<br />— NIAH-тест улучшился: 9/9 вместо 7/9. Пропуск деквантизации убирает квантизационный шум от позиций, которые ничего не вносят в результат.</blockquote><h2>Проблема — узкое место деквантизации</h2><p>Чтобы понять суть открытия, нужно разобраться в контексте. Современные LLM при генерации текста хранят промежуточные вычисления в так называемом <b>KV-кеше</b> (key-value cache). Это массив ключей (K) и значений (V), которые модель использует в механизме внимания — чтобы при генерации каждого нового токена не пересчитывать всю историю диалога.</p><p>Проблема: KV-кеш растёт линейно с длиной контекста и при 32K токенов занимает гигабайты памяти. Решение — <b>квантизация</b>: сжатие значений из 16-битного формата в 3-4 бита. <a href="https://research.google/blog/turboquant-redefining-ai-efficiency-with-extreme-compression/">TurboQuant</a> от Google (ICLR 2026) сжимает KV-кеш в 4,6 раза с потерей перплексии всего ~1%. Но при декодировании каждый сжатый блок нужно <b>деквантизовать</b> — преобразовать обратно в числа с плавающей точкой. И на длинных контекстах это становится узким местом.</p><p>На Apple M5 Max деквантизация TurboQuant занимает 14-34% времени декодирования. На контексте 32K токенов скорость декодирования падает с 78,3 tok/s (потолок без деквантизации) до 47,0 tok/s — штраф в 40%.</p><h3>14 попыток, которые провалились</h3><p>Прежде чем найти решение, Turney перебрал 14 альтернативных реализаций деквантизации на Apple Silicon: регистровые массивы, битовую арифметику, SIMD-шаффлы, FMA без ветвлений, слитые блочные операции. Ни одна не обогнала базовую таблицу подстановки (LUT) в константной памяти GPU.</p><p>Вывод оказался фундаментальным: на Apple Silicon 4 дивергентных чтения из константной памяти быстрее любой арифметики, которая даёт тот же результат. Это аппаратный пол — ниже него не опуститься. Единственный оставшийся рычаг — уменьшить количество позиций, которые нужно деквантизовать.</p><h2>Решение — не ускорять, а пропускать</h2><p>Ключевое наблюдение: в flash attention ядре веса внимания (softmax) вычисляются из ключей (K) <i>до того</i>, как начинается работа со значениями (V). То есть на момент деквантизации V мы уже знаем, какие позиции контекста получили значимый вес внимания, а какие — нет.</p><p><b>Что такое разреженность внимания?</b> Когда модель генерирует следующий токен, механизм внимания (softmax) распределяет вероятность по всем предыдущим позициям контекста. При коротком контексте внимание распределено относительно равномерно. Но чем длиннее контекст, тем более концентрированным оно становится: модель «смотрит» на несколько десятков ключевых позиций, а 90%+ получают веса ниже 10-6 — они практически ничего не вносят в итоговый результат.</p><p>Sparse V dequantization использует этот факт: если вес внимания для позиции ниже порога (10-6), её деквантизация и аккумуляция пропускаются целиком.</p><p>Вся реализация — три строки в Metal-ядре:</p><p>Это экономит чтения из константной памяти (обращения к LUT), ALU-операции (извлечение индексов, применение знака, умножение на норму) и чтения из глобальной памяти устройства.</p><p>Принципиальное отличие от 14 провалившихся подходов: они пытались сделать каждую из N операций быстрее (ограничено аппаратным полом). Sparse V убирает (1-p) x N операций целиком — и выигрыш растёт с длиной контекста, потому что разреженность внимания растёт.</p><h2>Результаты</h2><h3>Скорость декодирования (M5 Max, Qwen 3.5 35B MoE)</h3><p>Эффект sparse V масштабируется с длиной контекста — чем длиннее контекст, тем больше позиций пропускается:</p><ul><li>Короткий контекст: +1,4% (внимание плотное, мало что пропускать)</li><li>4K: +4,0%</li><li>8K: +7,2%</li><li>16K: +12,9%</li><li>32K: <b>+22,8%</b> (с 47,0 до 57,7 tok/s)</li></ul><p>При 32K токенов отношение к несжатому q8_0 улучшилось с 0,76x до 0,93x — почти паритет.</p><h3>Качество: перплексия и retrieval</h3><p>Перплексия при включении sparse V остаётся <b>численно идентичной</b> варианту без него. На корпусе wikitext-103 (50 чанков, контекст 32K, CI +/-0.021) дельта составила ровно 0,0000. Это подтверждено на всех протестированных длинах контекста: 8K, 16K, 32K.</p><p>Ещё более неожиданный результат — в тесте needle-in-a-haystack (NIAH). Этот тест проверяет, может ли модель найти конкретный факт, спрятанный в длинном контексте:</p><ul><li>Без sparse V: 7/9 (q8_0 тоже 7/9)</li><li>С sparse V: <b>9/9 (100%)</b></li></ul><p>Гипотеза автора: позиции с пренебрежимо малым весом внимания вносят практически нулевой полезный сигнал, но при деквантизации добавляют квантизационный шум. Sparse V убирает этот шум — и соотношение сигнал/шум улучшается.</p><h2>Почему это работает для любой квантизации</h2><p>Sparse V работает не на уровне конкретного формата квантизации, а на уровне <b>механизма внимания</b>. Метод использует веса softmax как сигнал для пропуска вычислений — а softmax одинаков вне зависимости от того, как именно сжаты K и V.</p><p>Автор валидировал метод на трёх форматах KV-кеша с разной стоимостью деквантизации:</p><ul><li><b>turbo3</b> (3,5 бита, TurboQuant) — +22,8% на 32K, перплексия без изменений</li><li><b>q8_0</b> (8 бит) — +5% к декодированию, перплексия идентична</li><li><b>q4_0</b> (4 бита) — перплексия идентична, скорость в пределах шума</li></ul><p>Закономерность: чем дороже деквантизация, тем больше выигрыш. turbo3 использует Walsh-Hadamard-преобразование и полярную декомпозицию — тяжёлые операции, поэтому выигрыш максимален. q4_0 использует простое масштабирование — пропуск дешёвой операции даёт мало. Но в обоих случаях метод <b>не ухудшает</b> ни перплексию, ни retrieval.</p><p>Это означает, что sparse V можно включить по умолчанию для любого формата квантизации KV-кеша в <a href="https://github.com/ggerganov/llama.cpp">llama.cpp</a> — как безусловную оптимизацию, которая ничего не стоит на коротком контексте и даёт ощутимый выигрыш на длинном.</p><h2>FAQ</h2><h3>Не сломает ли пропуск позиций качество генерации?</h3><p>Нет. Порог 10-6 выбран консервативно. При контексте 32K с 32 головами внимания вклад пропущенной позиции в итоговый вектор — менее одной миллионной от максимума. Перплексия во всех экспериментах осталась численно идентичной — дельта 0,0000 на 50 чанках wikitext-103.</p><h3>Почему нельзя пропускать и K-деквантизацию?</h3><p>Потому что K нужен для вычисления весов внимания. Чтобы узнать, какие позиции имеют пренебрежимо малый вес, нужно сначала деквантизовать все ключи и посчитать softmax. Автор отмечает это как направление будущей работы: двухпроходный механизм, где первый проход вычисляет приблизительные веса для фильтрации K-позиций.</p><h3>Работает ли это на NVIDIA GPU?</h3><p>Эксперименты проводились на Apple Silicon (M5 Max, M2 Pro). Реализация — Metal-ядро для llama.cpp. На CUDA профиль узкого места другой (NVIDIA использует тензорные ядра и HBM вместо unified memory), но сам принцип — пропуск работы для позиций с малым весом внимания — универсален. Портирование на CUDA и ROCm автор указывает как следующий шаг.</p><h2>Выводы</h2><p>Sparse V dequantization — элегантное решение, которое меняет вопрос с «как деквантизовать быстрее?» на «нужно ли деквантизовать вообще?». Метод использует информацию, которая уже есть в ядре flash attention — веса softmax — как бесплатный сигнал для пропуска вычислений.</p><p>Главная ценность подхода — не конкретный прирост в 22,8%, а более широкая идея <b>attention-aware computation</b>: использование собственных паттернов разреженности модели для оптимизации ядер инференса. Чем длиннее контекст, тем сильнее разреженность — и тем больше выигрыш. Именно при тех длинах контекста, где узкое место деквантизации наиболее болезненно.</p><p>Код и бенчмарки: <a href="https://github.com/TheTom/turboquant_plus">TheTom/turboquant_plus</a> (бенчмарки и диагностика), <a href="https://github.com/TheTom/llama-cpp-turboquant">TheTom/llama-cpp-turboquant</a> (реализация, ветка experimental_decode_speed_tests).</p><p>Обсуждение на <a href="https://www.reddit.com/r/LocalLLaMA/">r/LocalLLaMA</a>.</p><p><i>Источник: <a href="https://github.com/TheTom/turboquant_plus/blob/main/docs/papers/sparse-v-dequant.md">Sparse V Dequantization</a> — документация turboquant_plus.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Масштабирование монолита до 1 млн строк: уроки от тимлида до CTO</title>
      <link>https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto</link>
      <comments>https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto</guid>
      <description><![CDATA[<p>30 лучших уроков из 113 по масштабированию Django-монолита до 1 000 000 строк: архитектура, тестирование, мониторинг, безопасность, деплой и карьерный рост.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto">Масштабирование монолита до 1 млн строк: уроки от тимлида до CTO</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>Что можно узнать из монолита на миллион строк кода? Джек Кинселла проработал 7 лет техлидом и CTO в <a href="https://www.morphmarket.com/">MorphMarket</a> — маркетплейсе на стеке <a href="https://www.djangoproject.com/">Django</a> / <a href="https://react.dev/">React</a> (TS) / React Native с командой из 20 человек. За это время кодовая база выросла до 1 000 000 строк, а Джек прошёл путь от первого инженера до технического директора.</p><p>Он опубликовал 113 уроков, которые вынес из этого опыта. Мы отобрали самые ценные, сгруппировали по темам и адаптировали для русскоязычной аудитории. Каждый урок — практический: его можно взять и применить в своём проекте уже сегодня.</p><blockquote><b>Ключевые выводы</b><br /><br />— Кэширование — последняя мера. Сначала исправьте модель данных, индексы и N+1-запросы.<br />— E2E-тесты на happy path дают в 10 раз больше отдачи, чем юнит-тесты на отдельные методы.<br />— Доведите Sentry до inbox zero — это единственный самый мощный шаг для качества продукта.<br />— Не обвиняйте конкретного разработчика за баг в проде: это всегда каскад из 3+ ошибок системы.<br />— Деплой за 2 минуты вместо 28 — достижимо, и это меняет культуру принятия решений.</blockquote><h2>Про архитектуру и масштабирование</h2><h3>Используйте сервисные объекты вместо раздувания моделей</h3><p>В фреймворках, где модели строятся вокруг существительных (Product, User, Auction), логика неизбежно скапливается в этих классах. Со временем каждый из них превращается в монстра на тысячи строк. Решение — выносить процессы в отдельные сервисные объекты: PlaceAuctionBidService, ProductBillingService. Суть — перестать мыслить существительными и начать мыслить глаголами.</p><h3>Одна и та же вещь, реализованная дважды — провал технического лидерства</h3><p>Если в кодовой базе есть два разных клиента к одному платёжному провайдеру, три файла с функциями для работы с датами или дублирующиеся UI-компоненты — это симптом отсутствия ответственного за архитектуру. Создавайте очевидные, легко находимые места для общего кода (common/datetime.py, common/geo.py) и отмечайте переиспользуемый код на ревью.</p><h3>Полиморфные связи в БД — почти всегда ошибка</h3><p>ORM-код для полиморфных отношений становится крайне запутанным, и вы теряете защиту через foreign key constraints. Проще добавить nullable FK для каждой связанной модели и написать абстракции на уровне запросов.</p><h3>Дефолтные фильтры и сортировки в ORM — всегда ошибка</h3><p>Скрытые дефолты вроде order_by('-id') или filter(is_deleted=False) становятся миной замедленного действия, когда в команде 20+ человек. Новый разработчик не знает про неявное поведение — и получает баг. А скрытый ORDER BY портит аналитические запросы, потому что ORM добавляет ключ сортировки в SELECT.</p><h3>Вся инфраструктура — в коде</h3><p>Когда Джек пришёл в MorphMarket, все 27 крон-задач были настроены вручную на продакшн-сервере. Большинство отсутствовали на staging. Перенос в код (а затем конфигурация AWS/Heroku/CloudFlare/Sentry через <a href="https://www.terraform.io/">Terraform</a>) сделал все окружения похожими друг на друга — и, что важно, сделал инфраструктуру доступной для чтения AI-агентам.</p><h2>Про тестирование</h2><h3>E2E &gt; интеграционные &gt; юнит-тесты</h3><p>Пользователям важно, чтобы система работала целиком. Один E2E-тест на happy path каждой ключевой фичи (<i>регистрация, покупка, создание листинга</i>) даёт на порядок больше уверенности, чем десятки юнит-тестов. Детали дорабатывайте интеграционными и юнит-тестами — они быстрее и проще в поддержке.</p><h3>Автоматические тесты для шаблонных фич</h3><p>В MorphMarket написали систему, которая автоматически тестирует около 300 админ-страниц: парсит имя страницы, находит фабрику, создаёт запись в БД и проверяет, что страница открывается. Тот же подход можно применить к любому CRUD-эндпоинту без выделенного теста.</p><h3>Изолируйте тесты полностью</h3><p>Хрупкость тестов убивает мотивацию команды. Основные ловушки:</p><ul><li>Всё, что связано со временем — замораживайте или мокайте</li><li>Фиксируйте seed для всех источников случайности — в каждой библиотеке</li><li>Запретите реальные HTTP-запросы в не-E2E тестах (используйте VCR-подобные библиотеки)</li><li>Отдельный Redis для тестов с автоочисткой перед каждым тестом</li><li>Тестовая среда не должна читать ни одной переменной из вашего .env</li></ul><h3>Мокайте только внешние объекты</h3><p>Мок — полезный инструмент, но только для внешних зависимостей. Мокать внутренние методы тестируемого класса — антипаттерн. Если можно переписать тест без мока без потери скорости — так и сделайте.</p><h3>CI должен проверять не только ошибки, но и раздражающие мелочи</h3><p>В MorphMarket CI также ловит новые warning-и в тестах, рассинхронизацию SDK с бэкендом и забытые миграции в Django. Чем раньше замечена мелочь, тем дешевле её исправить.</p><h2>Про отладку и мониторинг</h2><h3>Sentry до inbox zero — самый мощный шаг к качеству</h3><p>Когда Джек пришёл в компанию, Sentry показывал 1 000 ошибок в час. После агрессивной фильтрации (игнор старых браузеров, расширений, пометка JavaScript-ошибок уникальным идентификатором) и починки реальных багов — меньше 1 ошибки в час. Результат: каждая новая ошибка стала <i>событием</i>, на которое команда реагирует немедленно.</p><h3>Dead Man Switch — мониторьте то, что должно происходить</h3><p>Исключения шумят и легко ловятся. А вот отсутствие события — нет. Если крон-система перестала работать или бэкапы БД не снимаются — вы узнаете об этом только через heartbeat-мониторы. Настройте их.</p><h3>Username в каждой строке лога</h3><p>По умолчанию в логах — только IP-адрес. Но IP ротируется, создаёт лишнее перенаправление при дебаге и не показывает картину при использовании нескольких устройств. Поместите текущий запрос в thread-local и добавьте username в каждую запись.</p><h3>Трипвайр-алерты на 20 самых важных эндпоинтов</h3><p>Деградация производительности подкрадывается незаметно — инкрементальные фичи постепенно ломают индексы и кэши. Установите порог в несколько стандартных отклонений от нормы на ключевых эндпоинтах. Команда узнает о проблеме в течение часов после деплоя, а не через неделю жалоб.</p><h3>Громко падайте при ошибках</h3><p>Не глушите исключения except: pass. Паттерн от Джека: <i>крашиться локально, мягко отказывать в проде — но всегда отправлять ошибку в Sentry/логи</i>. Чем раньше заметили — тем меньше ущерб.</p><h2>Про работу в команде</h2><h3>Не обвиняйте одного человека за баг в проде</h3><p>Любой инцидент — это каскад минимум трёх провалов: автор не заметил, ревьюер пропустил, QA не покрыло, тесты не проверили, архитектура допустила класс ошибки. Обвинение одного человека мешает команде видеть системные причины.</p><h3>Все разработчики должны быть немного фулстек</h3><p>В MorphMarket убрали строгое деление на фронтенд/бэкенд/ops. Переобучение заняло год, но окупилось: фронтендер добавляет поле в БД сам, а не ждёт бэкендера — это сокращает время доставки фич.</p><h3>Пятницы технического долга</h3><p>Разумный компромисс между CEO, который хочет фичи, и инженерами, которым нужна чистота кода: с понедельника по четверг приоритеты задаёт продукт, а по пятницам — CTO. Бонус: инженеры отдыхают на глубоких задачах перед выходными.</p><h3>Запретите длинные PR</h3><p>В MorphMarket был период, когда PR висели по 3 месяца и накапливали тысячи изменений. Их невозможно ревьюить, они конфликтуют с master и создают огромный деплой-риск. Новое правило: мерж в master каждые 1–2 дня, максимум — раз в неделю. Побочный эффект: старшие разработчики дают фидбэк раньше, и драматические переписывания случаются реже.</p><h3>Поощряйте общение в удалённых командах</h3><p>Джек начал с ежедневных постов о прогрессе, потом стал публично задавать вопросы (даже на которые знал ответ), делиться изменениями в коде и просить мнения. Это постепенно снизило барьер и сделало общение нормой, а не исключением.</p><h2>Про производительность</h2><h3>N+1-запросы — враг номер один в ORM</h3><p>Проблема проявляется на крупных эндпоинтах, которые постепенно расширяют разные авторы: новые фильтры и параметры создают спорадические N+1, которые возникают только при определённом сочетании фильтров и объёме данных. Настройте APM (например, <a href="https://newrelic.com/">New Relic</a>) для автоматического обнаружения N+1 и обучите команду ловить их на ревью.</p><h3>Кэширование — последняя мера</h3><p>Код кэширования чудовищно сложен в поддержке. Сначала исправьте модель данных, допишите индексы, уберите N+1, оптимизируйте алгоритмы. И только после этого — кэш. Джек рассказывает о случаях, когда наивный кэш работал на малых объёмах, но убивал Redis (он однопоточный!) при росте данных на два порядка.</p><h3>Таймауты на всех уровнях — иммунная система сервиса</h3><p>Веб-запрос должен завершиться за 25 секунд — иначе убивается. Да, для длинных задач придётся использовать фоновые задачи, но без таймаутов одна тормозящая внешняя API может съесть все потоки сервера и уронить всё.</p><h3>Выгружайте работу в фоновые задачи</h3><p>Отправка email, push-уведомления, обработка вебхуков от платёжных систем — всё это должно уходить в очередь. Веб-процесс обязан оставаться быстрым.</p><h3>ORDER BY id вместо ORDER BY created</h3><p>Если поле created неизменяемо и совпадает по порядку с id — сортируйте по id. Индекс на id уже есть бесплатно в каждой таблице.</p><h3>Удаляйте ненужные данные</h3><p>В MorphMarket некоторые таблицы копили 10 лет данных — логи IP-адресов, записи push-уведомлений, сообщения девятилетней давности. Скрипт очистки делает эти таблицы компактнее и быстрее.</p><h2>Про безопасность и надёжность</h2><h3>Начните с модели угроз: что может получить атакующий?</h3><p>Для MorphMarket основные вектора: захват аккаунтов (фикс — обязательный 2FA), спам-фишинг (капча на регистрации + фильтры сообщений), DDoS (<a href="https://www.cloudflare.com/">Cloudflare</a> + rate limit на nginx + кэш ключевых эндпоинтов). Универсального подхода нет — каждый сервис должен понимать свою специфику.</p><h3>CHECK-ограничения в БД — лучше предотвратить, чем чинить</h3><p>Раньше в MorphMarket крон-задача раз в сутки проверяла целостность данных и слала алерты. Всё заменили на CHECK constraints на уровне БД — некоторые проверяют до 16 операций над разными колонками. Невалидные данные просто не могут быть записаны.</p><h3>Вебхуки — рассадник race conditions</h3><p>Действие в коде может вызвать вебхук, который прилетит быстрее, чем данные запишутся в БД. Получатель вебхука прочитает устаревшее состояние и может затереть свежие данные. Решение: откладывайте действия, вызывающие вебхуки, до on_commit в БД.</p><h3>Логируйте намерение перед побочным эффектом — особенно для платежей</h3><p>Перед списанием с карты или возвратом пишите в лог: <i>"bill user XYZ $100 for Y"</i>. Простой Write-Ahead Log спасал MorphMarket в нескольких инцидентах.</p><h2>Про деплой</h2><h3>Деплой должен быть быстрым — действительно быстрым</h3><p>MorphMarket сократил деплой с 28 минут до 2. Как: разделили фронтенд и бэкенд, распараллелили, перешли на Rust-based компиляцию JS, вынесли сборку на мощные машины (MacBook M-series вместо commodity Heroku), убрали из слага всё ненужное, почистили старые ассеты из S3.</p><h3>Smoke-тест после каждого деплоя в прод</h3><p>Автоматическая проверка ключевых страниц через <a href="https://playwright.dev/">Playwright</a> после каждого деплоя. Если последние 150 деплоев были скучными — соблазн перестать проверять вручную огромен. Автоматический smoke-тест не устаёт и не теряет бдительность.</p><h3>Не деплойте ничего рискованного в пятницу</h3><p>Баг может проявиться вечером или в субботу, когда никого нет. Мелкие хотфиксы и изменения в админке — можно. Всё остальное — до четверга.</p><h3>Плейбук для инцидентов при деплое</h3><p>Короткие инструкции: как убить залипшие соединения к БД, точечно почистить кэш (без flush, который разлогинит всех), форсировать обновление ассетов. Обученная команда сокращает даунтайм в разы.</p><h2>Про карьерный рост — от тимлида до CTO</h2><h3>Научитесь говорить о производительности сотрудников</h3><p>Джек признаётся, что панически боялся этих разговоров, потому что не любит конфликты. Но реальность проста: лучше открыто сказать человеку, где он не дотягивает, чем копить раздражение и уволить неожиданно. Большинство людей способны расти под менторингом — Джек это недооценивал.</p><h3>Документ ожиданий от сотрудника</h3><p>Если написать <i>"мы ценим, когда вы решаете проблемы в #bug-discussion до того, как они дойдут до CTO"</i>, люди действительно начнут так делать. Мотивированный сотрудник хочет ясности. Он не может прочитать ваши мысли.</p><h3>Одно крупное изменение за раз</h3><p>Внедряете E2E-тесты? Доведите до стабильности, прежде чем браться за линтеры. Если система сделана плохо — команда не будет ей доверять и не будет использовать. Плюс есть предел того, сколько изменений люди могут усвоить одновременно.</p><h3>CLI-инструменты + AI = максимальный рычаг</h3><p>AI отлично работает с текстом. CLI-инструменты тоже. Дайте AI доступ к продакшн-логам, отчётам об ошибках, read-only реплике БД, CI-результатам и REPL — и получите огромный множитель продуктивности. Джек называет это <i>"наблюдаемость через CLI — ваш главный усилитель для AI"</i>.</p><h2>FAQ</h2><h3>Почему монолит, а не микросервисы?</h3><p>Монолит на миллион строк с 20 разработчиками — рабочая модель, если соблюдать дисциплину. Микросервисы добавляют сетевую сложность, и для команды такого размера overhead координации перевешивает выгоды. Джек не утверждает, что микросервисы плохи — он утверждает, что переход оправдан только при конкретной боли, а не как дефолтный выбор.</p><h3>С чего начать, если в проекте уже бардак?</h3><p>С Sentry до inbox zero и таймаутов на веб-запросы. Первое даёт видимость реальных проблем, второе предотвращает каскадные отказы. Дальше — по статье: N+1, изоляция тестов, сервисные объекты.</p><h3>Насколько эти уроки применимы за пределами Django?</h3><p>Джек 17 лет работал с Rails, Laravel, Express и другими фреймворками — и подчёркивает, что большинство уроков переносимы. N+1, дефолтные сортировки, race conditions в вебхуках, инфраструктура как код — всё это не привязано к конкретному стеку.</p><h3>Правда ли, что E2E-тесты лучше юнит-тестов?</h3><p>Не <i>лучше</i>, а дают больше отдачи на первом этапе. Один E2E-тест на happy path покрывает весь стек. Юнит-тесты нужны для деталей и граничных случаев, но начинать покрытие лучше сверху вниз.</p><h2>Выводы</h2><p>113 уроков Джека Кинселлы — это не теория из книг, а выжимка из 7 лет ежедневной работы над монолитом, который обслуживает реальных пользователей. Главные принципы:</p><ul><li>Предотвращайте, а не чините — CHECK constraints, таймауты, линтеры</li><li>Делайте невидимое видимым — логи, мониторинг, Sentry до inbox zero</li><li>Инвестируйте в скорость обратной связи — быстрый деплой, быстрый CI, короткие PR</li><li>Доверяйте людям и системе, а не контролю — fullstack-навыки, плейбуки, документ ожиданий</li></ul><p>Оригинальная статья: <a href="https://www.semicolonandsons.com/articles/scaling-a-monolith-to-1m-loc-113-pragmatic-lessons-from-tech-lead-to-cto">Scaling a Monolith to 1M LOC: 113 Pragmatic Lessons from Tech Lead to CTO</a> (Semicolon &amp; Sons, Jack Kinsella).</p><p>Если знаете коллегу, который борется с растущим монолитом — отправьте ему эту статью. А в комментариях расскажите, какой урок оказался самым неожиданным для вас.</p>]]></content:encoded>
    </item>
    <item>
      <title>Переписали JSONata на Go с помощью ИИ за день — экономия $500K в год</title>
      <link>https://tproger.ru/translations/perepisali-jsonata-na-go-s-pomoshhyu-ai-za-den---ekonomiya--500k-v</link>
      <comments>https://tproger.ru/translations/perepisali-jsonata-na-go-s-pomoshhyu-ai-za-den---ekonomiya--500k-v?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/perepisali-jsonata-na-go-s-pomoshhyu-ai-za-den---ekonomiya--500k-v</guid>
      <description><![CDATA[<p>Инженер из Reco переписал JSONata на Go за 7 часов с ИИ. Библиотека gnata дала ускорение до 1000x и сэкономила $500 000 в год на инфраструктуре Kubernetes.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/perepisali-jsonata-na-go-s-pomoshhyu-ai-za-den---ekonomiya--500k-v">Переписали JSONata на Go с помощью ИИ за день — экономия $500K в год</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>Один инженер. Семь часов работы. $400 на токены для ИИ. Результат — чистая Go-библиотека, которая заменила целый флот Node.js-подов в Kubernetes и сэкономила компании $500 000 в год на инфраструктуре.</p><p>Это не абстрактный кейс из маркетингового блога. Это реальная история от <a href="https://www.reco.ai/">Reco</a> — SaaS-платформы безопасности, которая обрабатывает миллиарды событий в день. Их инженер Нир Барак взял подход, описанный Cloudflare в статье о <a href="https://blog.cloudflare.com/how-we-rebuilt-nextjs-with-ai-in-one-week/">переписывании Next.js с помощью ИИ</a>, и применил его к собственной инфраструктуре. Получилась библиотека <a href="https://github.com/RecoLabs/gnata">gnata</a> — полная реализация JSONata 2.x на чистом Go с ускорением до 1000x на типовых выражениях.</p><p><a href="https://jsonata.org/">JSONata</a> — это язык запросов и трансформации JSON-данных. Его можно представить как jq, но с поддержкой лямбда-функций и более выразительным синтаксисом. JSONata позволяет писать сложные правила обработки данных без необходимости лезть в код основного приложения. Это делает его популярным в системах, где бизнес-аналитики или исследователи пишут правила детекции, а инженеры обеспечивают инфраструктуру.</p><blockquote>Главное из статьи:<br /><br />- JSONata-выражения через RPC обходились в $300 000/год на инфраструктуру<br />- ИИ-ассистированная перезапись на Go заняла 7 часов и стоила $400 в токенах<br />- Результат: 13 000 строк Go-кода, 1 778 пройденных тестов из официального набора<br />- Ускорение до 1000x на простых выражениях за счёт устранения RPC и JSON-парсинга<br />- Суммарная экономия после оптимизации пайплайна — $500 000/год</blockquote><h2>Проблема — JSONata как узкое место</h2><p>У Reco есть policy engine, который проверяет JSONata-выражения на каждом событии в потоке данных. Миллиарды событий, тысячи различных выражений. Исследователи безопасности пишут правила детекции на JSONata, а инженеры обеспечивают их выполнение в реальном времени.</p><p>Загвоздка: <b>референсная реализация JSONata написана на JavaScript</b>, а весь пайплайн Reco — на Go. Годами компания держала в Kubernetes отдельный флот Node.js-подов (jsonata-js), к которым Go-сервисы обращались через RPC.</p><p>Для каждого события и каждого выражения это означало:</p><ol><li>Сериализовать данные на стороне Go</li><li>Отправить по сети в Node.js-под</li><li>Выполнить JSONata-выражение</li><li>Сериализовать результат</li><li>Отправить обратно в Go-сервис</li></ol><h3>Во что это обходилось</h3><p>Прямые затраты на инфраструктуру jsonata-js составляли <b>около $300 000 в год</b>, и сумма росла с каждым новым клиентом и правилом детекции. На одном из крупных кластеров количество реплик jsonata-js перевалило за 200 — и команда столкнулась с лимитом IP-адресов в Kubernetes.</p><p>Но денежные затраты были даже не главной проблемой. RPC round-trip занимает ~150 микросекунд ещё до начала вычисления. Для простого выражения вроде user.email = "admin@co.com", которое должно выполняться за наносекунды, основная цена — пересечение языковой границы. На масштабе Reco эти микросекунды складываются в ощутимые задержки.</p><p>Команда пробовала разные подходы: оптимизировала выражения, кэшировала результаты, даже встраивала V8 прямо в Go-процесс. Всё это давало инкрементальные улучшения, но не решало корневую проблему — <b>данные всё равно пересекали языковую границу</b>.</p><h2>Решение — переписать на Go с помощью ИИ</h2><p>Толчком стала статья Cloudflare о том, как один инженер за неделю переписал API Next.js на Vite, потратив $1 100 на токены. Нир Барак прочитал её и понял: у Reco та же самая ситуация — есть спецификация, есть тестовый набор, нужна реализация на другом языке.</p><h3>Подход: спецификация + тесты = контракт</h3><p>Метод прямолинейный:</p><ol><li>Портировать официальный тестовый набор jsonata-js на Go</li><li>Направить ИИ на реализацию, пока все тесты не пройдут</li><li>Добавить оптимизации, специфичные для Go и задачи Reco</li></ol><p>В выходные Нир составил план, разбитый на «волны» (с помощью ИИ). На следующий день нажал «старт».</p><h3>Инструменты и затраты</h3><p>Конкретные модели и инструменты автор не раскрывает, но ключевые цифры говорят сами за себя:</p><ul><li><b>Время работы:</b> 7 часов (один инженер)</li><li><b>Стоимость токенов:</b> $400</li><li><b>Результат:</b> 13 000 строк Go-кода</li><li><b>Тесты:</b> 1 778 пройденных тест-кейсов из официального набора jsonata-js</li></ul><p>Библиотека получила название <a href="https://github.com/RecoLabs/gnata">gnata</a> — полная реализация спецификации JSONata 2.x на чистом Go.</p><h2>Технические результаты</h2><h3>Двухуровневая архитектура вычислений</h3><p>gnata использует двухуровневую архитектуру оценки выражений. На этапе компиляции каждое выражение анализируется и классифицируется.</p><p><b>Быстрый путь (fast path)</b> обрабатывает простые выражения: поиск по полям, сравнения и 21 встроенную функцию на чистых путях (например, $exists(a.b) или $lowercase(name)). Эти выражения вычисляются прямо по сырым JSON-байтам, без полного парсинга документа. Для выражения вроде account.status = "active" результат — <b>ноль аллокаций в куче</b>.</p><p><b>Полный путь (full path)</b> — полноценный парсер и вычислитель с полной семантикой JSONata 2.x. Он парсит JSON, но только нужные поддеревья, а не весь документ целиком.</p><h3>Потоковая обработка</h3><p>Поверх двухуровневой оценки работает потоковый слой (StreamEvaluator), спроектированный под конкретную задачу Reco: применить N скомпилированных выражений к каждому событию, где события структурно похожи.</p><ul><li>Все пути из всех выражений объединяются в один проход по данным — количество выражений не влияет на скорость чтения</li><li>После прогрева горячий путь работает без блокировок (lock-free)</li><li>Планы вычислений создаются один раз на схему событий и кэшируются неизменяемо — чтение через один атомарный load без синхронизации</li><li>Память ограничена: кэш с настраиваемой ёмкостью, вытесняющий старые записи</li></ul><h3>Бенчмарки</h3><p>Ускорение на простых выражениях — в основном за счёт полного устранения RPC: gnata вычисляет прямо по сырым байтам без парсинга JSON. На сложных выражениях, где нужен полный парсинг и AST-вычисление, разрыв сужается, но они всё равно <b>в 25–90 раз быстрее</b>, чем через RPC.</p><p>На простых lookups ускорение достигает <b>1000x</b>.</p><h3>Пример использования</h3><h2>Экономический эффект</h2><h3>Этап 1: устранение RPC-флота</h3><p>Первый и самый очевидный эффект — полное устранение jsonata-js подов в Kubernetes:</p><ul><li><b>До:</b> ~$25 000/мес на compute для jsonata-js (200+ реплик Node.js)</li><li><b>После:</b> $0 — gnata работает как библиотека внутри существующих Go-сервисов</li><li><b>Экономия:</b> ~$300 000/год</li></ul><h3>Этап 2: рефакторинг rule engine</h3><p>Но на этом история не закончилась. Старый JSONata через RPC мог обрабатывать только одно выражение за раз, и вся инфраструктура вокруг него была вынуждена подстраиваться. Rule engine запускал десятки тысяч горутин для максимизации параллелизма — со всеми вытекающими: избыточное потребление памяти и высокая конкуренция за CPU.</p><p>gnata не имеет таких ограничений. Команда заменила внутренности rule engine на более простую и эффективную реализацию с just-in-time батчингом (паттерн request coalescing), короткоживущими кэшами и групповыми запросами обогащения данных.</p><p>Результат: ещё <b>~$18 000/мес</b>, или <b>~$200 000/год</b> экономии.</p><h3>Итого</h3><p><b>$500 000/год</b> снято с пайплайна за две недели работы. Из них 7 часов — создание gnata, остальное — тестирование, code review и раскатка.</p><h2>Как внедряли — от shadow mode до продакшена</h2><p>Создать библиотеку — половина дела. Вторая половина — убедиться, что она выдаёт ровно те же результаты, что и оригинал, на миллиардах реальных событий.</p><p>У Reco уже была инфраструктура для shadow-тестирования: feature flags, параллельное вычисление, логирование расхождений. Подключить gnata к ней было несложно.</p><p>Хронология раскатки:</p><ul><li><b>День 1:</b> gnata готова, PR открыт</li><li><b>Дни 2–6:</b> code review, QA на реальных продакшен-выражениях, деплой в preprod в shadow mode. gnata вычисляет всё, но результаты jsonata-js остаются основными. Расхождения логируются и алертятся. Найденные edge-кейсы исправляются</li><li><b>День 7:</b> три дня подряд ноль расхождений. gnata переведена в primary</li></ul><p>К моменту перевода gnata уже обработала миллиарды событий и выдала идентичные результаты. Более того, в процессе обнаружились баги в самом jsonata-js — случаи, где референсная реализация не соответствует собственной спецификации.</p><p>Побочный эффект: gnata стала одним из первых крупных PR в Reco, где ИИ-агенты ревьюили ИИ-сгенерированный код. Агенты помечали всё подряд — и реальные проблемы с конкурентностью, и косметические замечания. Команде пришлось научить их отличать одно от другого, и этот опыт теперь используется шире.</p><h2>Уроки — когда ИИ-переписывание работает</h2><p>gnata — удачный кейс. Но не каждая задача подходит для ИИ-переписывания. Вот что сделало этот проект идеальным кандидатом:</p><ol><li><b>Чёткая спецификация.</b> JSONata имеет формальную спецификацию и обширный тестовый набор. ИИ не нужно придумывать поведение — нужно реализовать заданное</li><li><b>Детерминированная проверка.</b> Каждое выражение имеет однозначный правильный ответ. Можно автоматически проверить корректность — не нужен человек для оценки каждого результата</li><li><b>Глубокая экспертиза инженера.</b> Нир не просто «нажимал кнопку». Он спроектировал двухуровневую архитектуру, потоковую обработку и стратегию кэширования. ИИ генерировал код, но архитектурные решения принимал человек</li><li><b>Существующая инфраструктура тестирования.</b> Shadow mode, feature flags, сравнение результатов — всё это было готово до gnata. Без этого раскатить ИИ-сгенерированный код в продакшен было бы рискованно</li><li><b>Ограниченная область.</b> JSONata — это чётко ограниченная задача. Не «перепишите весь бэкенд», а «реализуйте этот конкретный язык на другом стеке»</li></ol><p>Как отметил Андрей Карпати: программирование становится неузнаваемым, и на верхних уровнях глубокая техническая экспертиза — «ещё больший мультипликатор, чем раньше, из-за увеличенного рычага». gnata — хорошая иллюстрация этой мысли.</p><h2>FAQ</h2><h3>Что такое JSONata и зачем он нужен?</h3><p>JSONata — это язык запросов и трансформации для JSON-данных. Он позволяет писать выражения для фильтрации, преобразования и агрегации JSON без написания кода на языке общего назначения. Используется в IoT-платформах, ETL-пайплайнах и системах обработки событий.</p><h3>Почему нельзя было просто оптимизировать существующий подход?</h3><p>Reco пробовали: оптимизация выражений, кэширование, встраивание V8 в Go. Всё это давало инкрементальные улучшения, но корневая проблема — пересечение языковой границы через RPC — оставалась. Единственное радикальное решение — нативная реализация на Go.</p><h3>Насколько gnata совместима с оригинальной JSONata?</h3><p>gnata проходит 1 778 тест-кейсов из официального набора jsonata-js и 2 107 интеграционных тестов в продакшен-обёртке Reco. В процессе тестирования были обнаружены баги в самом jsonata-js, где референсная реализация не соответствует собственной спецификации.</p><h3>Можно ли повторить этот подход в другом проекте?</h3><p>Да, если есть формальная спецификация и тестовый набор. Подход Cloudflare (vinext) и Reco (gnata) одинаков: берём спеку + тесты, направляем ИИ на реализацию до прохождения всех тестов. Ключевое требование — детерминированная проверка результата.</p><h2>Выводы</h2><p>История gnata — это не про то, что ИИ «волшебным образом» пишет продакшен-код. Это про то, как опытный инженер с глубоким пониманием задачи использует ИИ как мультипликатор своей экспертизы.</p><p>Ключевые выводы:</p><ul><li><b>ИИ-переписывание работает</b>, когда есть чёткая спецификация и автоматизированная проверка</li><li><b>Экономический эффект может быть огромным:</b> $400 на токены превратились в $500 000/год экономии</li><li><b>Архитектура важнее генерации кода:</b> двухуровневая оценка, потоковая обработка и батчинг — решения инженера, не ИИ</li><li><b>Shadow mode обязателен:</b> ИИ-сгенерированный код нельзя просто «выкатить» — нужна параллельная проверка на реальных данных</li><li><b>ROI считается просто:</b> если задача имеет спеку и тесты, стоимость ИИ-переписывания предсказуема</li></ul><p>gnata доступна как open-source библиотека: <a href="https://github.com/RecoLabs/gnata">github.com/RecoLabs/gnata</a>.</p><p><i>Источник: <a href="https://reco.ai/blog/we-rewrote-jsonata-with-ai">We Rewrote JSONata with AI</a> — блог Reco.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Одна строчка в Kubernetes сэкономила Cloudflare 600 часов в год</title>
      <link>https://tproger.ru/translations/odna-strochka-v-kubernetes-sekonomila-cloudflare-600-chasov-v-god</link>
      <comments>https://tproger.ru/translations/odna-strochka-v-kubernetes-sekonomila-cloudflare-600-chasov-v-god?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/odna-strochka-v-kubernetes-sekonomila-cloudflare-600-chasov-v-god</guid>
      <description><![CDATA[<p>Инженеры Cloudflare обнаружили, что fsGroup рекурсивно менял права на миллионах файлов при каждом рестарте Atlantis. Фикс — одна строчка fsGroupChangePolicy.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/odna-strochka-v-kubernetes-sekonomila-cloudflare-600-chasov-v-god">Одна строчка в Kubernetes сэкономила Cloudflare 600 часов в год</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Облачные технологии]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>Представьте: каждый раз, когда вы перезапускаете критически важный сервис, вся команда сидит без дела 30 минут. Никаких изменений в инфраструктуре, никаких деплоев — просто ожидание. И так 100 раз в месяц. Именно с этим столкнулась команда <a href="https://www.cloudflare.com/">Cloudflare</a>, управляющая десятками Terraform-проектов через <a href="https://www.runatlantis.io/">Atlantis</a>. Причиной оказалась одна настройка Kubernetes, которая незаметно превратилась в бутылочное горлышко по мере роста данных.</p><p>Разбираемся, как инженеры Cloudflare нашли проблему и решили её буквально одной строчкой конфигурации.</p><blockquote><b>Ключевые выводы:</b><br />— <b>fsGroup</b> в Kubernetes рекурсивно меняет владельца всех файлов на PersistentVolume при каждом монтировании — это безопасный дефолт, который становится проблемой на больших томах.<br />— Параметр <b>fsGroupChangePolicy: OnRootMismatch</b> (доступен с K8s 1.20) проверяет только корневую директорию вместо обхода всех файлов.<br />— Одна строчка в манифесте сократила время рестарта Atlantis с 30 минут до 30 секунд — экономия ~600 инженерных часов в год.<br />— Безопасные дефолты Kubernetes рассчитаны на небольшие рабочие нагрузки. При масштабировании их стоит пересматривать.</blockquote><h2>Проблема — 30 минут на рестарт</h2><p><a href="https://www.runatlantis.io/">Atlantis</a> — это инструмент для автоматизации Terraform через pull/merge request'ы. В Cloudflare он управляет десятками проектов и работает как singleton StatefulSet в Kubernetes. Для хранения состояния репозиториев Atlantis использует PersistentVolume (PV).</p><p>Перезапуски нужны регулярно: ротация секретов, onboarding новых проектов, обновления конфигурации. С учётом примерно 100 рестартов в месяц каждый 30-минутный простой складывался в <b>50+ часов заблокированного инженерного времени ежемесячно</b>. Плюс каждый рестарт триггерил алерт и будил дежурного.</p><p>Ситуация обострилась, когда на PV закончились inode'ы. Inode — это запись о файле или директории в файловой системе, и их количество фиксируется при создании ФС. Единственный способ получить больше inode'ов в их конфигурации — увеличить размер тома, а для этого нужен рестарт пода.</p><h2>Расследование</h2><h3>Первые зацепки</h3><p>После kubectl rollout restart statefulset atlantis новый под появлялся практически мгновенно, но зависал на стадии инициализации:</p><p>События пода выглядели нормально — Kubernetes успешно распределил под на ноду, скачал образ init-контейнера за доли секунды. Но между назначением на ноду и началом скачивания образа был необъяснимый промежуток в 30 минут.</p><h3>Копаем глубже — логи kubelet</h3><p>Событий Kubernetes было недостаточно. Инженеры обратились к логам kubelet — компонента, который на каждой ноде координирует создание подов, монтирование томов и другие операции. Поскольку kubelet работает как systemd-сервис, его логи доступны через централизованную систему (в данном случае Kibana).</p><p>В логах kubelet было видно, что PersistentVolume монтируется сразу после назначения пода. Секреты тоже монтируются без проблем. Но затем — снова провал, и в логах появляется ошибка:</p><p>Kubelet считал, что под готов к запуску, но что-то блокировало монтирование тома и вызывало таймаут.</p><h3>Разгадка — fsGroup</h3><p>Последней зацепкой стало имя PV. Инженеры использовали его как поисковый запрос в Kibana — и сразу нашли ключевое сообщение:</p><p>Вот в чём было дело. В spec.securityContext пода было указано fsGroup: 1. Это нужно, чтобы процессы Atlantis (запущенные не от root) имели доступ к файлам на томе. Но способ, которым Kubernetes это обеспечивает, оказался проблемой: <b>при каждом монтировании</b> kubelet выполняет рекурсивный chgrp по всем файлам и директориям на PersistentVolume.</p><p>А файлов к тому моменту были миллионы — команда недавно столкнулась с нехваткой inode'ов, что прямо указывало на огромное количество файлов. Рекурсивный обход миллионов записей на каждом рестарте — вот что съедало 30 минут.</p><h2>Решение — одна строчка</h2><p>Начиная с версии 1.20, Kubernetes поддерживает поле fsGroupChangePolicy в pod.spec.securityContext. По умолчанию оно установлено в Always — рекурсивный обход при каждом монтировании. Альтернативное значение OnRootMismatch проверяет только корневую директорию тома: если у неё уже правильная группа, рекурсивный обход не запускается.</p><p>Фикс целиком:</p><p><b>Важно:</b> устанавливать OnRootMismatch стоит только если вы уверены, что ничто не меняет группу файлов на PersistentVolume извне. В случае Cloudflare инженеры проверили, что никакие процессы не модифицируют права на файлы в томе, и только после этого применили настройку.</p><h2>Почему это работает</h2><p>Параметр fsGroup в Kubernetes решает конкретную задачу: контейнеры, запущенные от непривилегированного пользователя, должны иметь доступ к файлам на примонтированном томе. Kubernetes обеспечивает это, выполняя chgrp -R по всему содержимому PV при каждом монтировании.</p><p>Для небольших томов с сотнями или тысячами файлов это работает незаметно. Но по мере роста данных время обхода растёт линейно. На миллионах файлов — даже на быстрых NVMe-дисках — операция занимает десятки минут.</p><p>Политика OnRootMismatch меняет логику: kubelet проверяет только корневую директорию тома. Если её группа совпадает с fsGroup, рекурсивный обход пропускается. Поскольку Atlantis сам создаёт файлы с правильной группой (она наследуется от корневой директории), проверять миллионы файлов при каждом рестарте нет необходимости.</p><h2>Результат</h2><ul><li><b>Время рестарта:</b> 30 минут → 30 секунд (ускорение в 60 раз)</li><li><b>Экономия:</b> ~50 часов заблокированного инженерного времени в месяц</li><li><b>В годовом исчислении:</b> ~600 часов продуктивной работы, возвращённых команде</li><li><b>Ложные алерты:</b> дежурный инженер больше не получает пейджер на каждый рестарт</li><li><b>Время на фикс:</b> одна строчка в YAML-манифесте</li></ul><p>Как отметили в Cloudflare: диагностика заняла больше времени, чем само исправление.</p><h2>Уроки для разработчиков</h2><ol><li><b>Проверяйте дефолты при масштабировании.</b> Безопасные настройки по умолчанию в Kubernetes рассчитаны на простые рабочие нагрузки. Когда данные растут, дефолты могут незаметно стать бутылочным горлышком.</li><li><b>Ищите не только в событиях Kubernetes.</b> Pod events показывают не всё. Логи kubelet, метрики нод, журналы CSI-драйверов — полезные источники информации при отладке проблем с монтированием.</li><li><b>Считайте стоимость «мелких» проблем.</b> 30 минут простоя кажутся терпимыми. 30 минут x 100 раз в месяц = 50 часов. Масштаб меняет приоритеты.</li><li><b>Аудит securityContext.</b> Поля fsGroup, runAsUser, runAsGroup и fsGroupChangePolicy напрямую влияют на время старта подов с PersistentVolume. Если у вас большие тома — проверьте эти настройки.</li><li><b>Не всякий фикс должен быть сложным.</b> Самые ценные исправления часто оказываются самыми простыми — но требуют глубокого понимания того, как система работает под капотом.</li></ol><h2>Частые вопросы</h2><p><b>Что делает fsGroupChangePolicy в Kubernetes?</b></p><p>Параметр fsGroupChangePolicy определяет, когда Kubernetes рекурсивно обновляет группу-владельца файлов на PersistentVolume. Значение Always (по умолчанию) запускает рекурсивный chgrp при каждом монтировании. Значение OnRootMismatch выполняет обновление только если группа корневой директории не совпадает с fsGroup. Поддерживается с Kubernetes 1.20.</p><p><b>Безопасно ли использовать OnRootMismatch?</b></p><p>Если файлы на PersistentVolume создаются только процессами внутри пода (и наследуют правильную группу от корневой директории), OnRootMismatch безопасен. Но если внешние процессы или другие поды могут менять группу файлов на томе, лучше оставить значение Always и искать другие способы оптимизации.</p><p><b>Как узнать, влияет ли fsGroup на время старта моих подов?</b></p><p>Проверьте логи kubelet на ноде, где запускается под. Если в них есть сообщение <i>"Setting volume ownership... If the volume has a lot of files then setting volume ownership could be slow"</i>, значит рекурсивный обход выполняется. Также можно сравнить время между событиями Scheduled и Started для пода — большой разрыв при наличии PV с fsGroup указывает на эту проблему.</p><h2>Выводы</h2><p>История Cloudflare — отличный пример того, как безопасный дефолт может превратиться в серьёзную проблему на масштабе. fsGroupChangePolicy: Always — разумная настройка для небольших томов, но на миллионах файлов она съедает минуты при каждом рестарте.</p><p>Главный урок: если что-то в вашем кластере работает медленнее, чем должно — не расширяйте окно алертов, а копайте глубже. Иногда ответ — это одна строчка YAML.</p><p><i>По материалам блога <a href="https://blog.cloudflare.com/one-line-kubernetes-fix-saved-600-hours-a-year/">Cloudflare</a>.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Google TurboQuant простыми словами: как сжать нейросеть в 6 раз и запустить на MacBook</title>
      <link>https://tproger.ru/articles/google-turboquant-prostymi-slovami--kak-szhat-nejroset-v-6-raz-</link>
      <comments>https://tproger.ru/articles/google-turboquant-prostymi-slovami--kak-szhat-nejroset-v-6-raz-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/google-turboquant-prostymi-slovami--kak-szhat-nejroset-v-6-raz-</guid>
      <description><![CDATA[<p>TurboQuant от Google сжимает KV-кеш LLM до 3 бит — ускорение до 8× на H100, Qwen на MacBook Air. Объясняем как работает и что это значит для вас.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/google-turboquant-prostymi-slovami--kak-szhat-nejroset-v-6-raz-">Google TurboQuant простыми словами: как сжать нейросеть в 6 раз и запустить на MacBook</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[GPU]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 04:45:23 GMT</pubDate>
      <content:encoded><![CDATA[<p>На этой неделе в мире ИИ произошло землетрясение. Google Research опубликовал алгоритм TurboQuant, который сжимает потребление памяти нейросетей при инференсе в 6 раз и ускоряет вычисления в 8 раз — без потери качества. <a href="https://tproger.ru/news/google-turboquant-obvalil-akcii-proizvoditelej-pamyati-na-25----r">Акции производителей памяти обвалились на 25%</a>. Reddit кипит. Все суетятся — но мало кто реально понимает, что именно сделали в Google и почему это важно.</p><p>Мы разобрались. Объясняем так, чтобы было понятно всем — и тем, кто пишет нейросети, и тем, кто просто хочет запустить ChatGPT-подобную модель на своём ноутбуке.</p><p><b>TurboQuant</b> — алгоритм квантизации KV-кеша для больших языковых моделей (LLM), разработанный Google Research. Сжимает KV-кеш с 16 до 3 бит на значение, уменьшая потребление памяти при инференсе в 5–6 раз без переобучения модели и без потери качества. Представлен на ICLR 2026 (<a href="https://arxiv.org/abs/2504.19874">arXiv:2504.19874</a>).</p><h2>KV-кеш: почему нейросетям нужно столько памяти</h2><p>Когда вы общаетесь с ChatGPT, Gemini или любой другой LLM, модель должна «помнить» весь ваш диалог. Для этого она использует <b>KV-кеш</b> (Key-Value cache) — специальный буфер в памяти видеокарты, где хранятся промежуточные вычисления механизма внимания (attention).</p><p>Представьте: вы читаете книгу и делаете заметки на полях. KV-кеш — это те самые заметки. Без них модели пришлось бы каждый раз перечитывать весь текст с начала, чтобы ответить на ваш вопрос.</p><p>Проблема в масштабе:</p><ul><li>Модель Llama 3.1 70B при контексте 128K токенов тратит ~40 ГБ только на KV-кеш — это больше, чем весит сама модель в квантизированном виде</li><li>Чем длиннее контекст (переписка, документ, код) — тем больше памяти нужно, и это растёт линейно</li><li>Именно KV-кеш — главный ограничитель того, сколько текста модель может «видеть» одновременно</li></ul><p>И именно KV-кеш сжимает TurboQuant.</p><h2>Что такое TurboQuant</h2><p>TurboQuant — это алгоритм сжатия KV-кеша, разработанный Google Research. <a href="https://arxiv.org/abs/2504.19874">Статья</a> принята на ICLR 2026 — одну из топовых конференций по машинному обучению. Авторы — Amir Zandieh, Vahab Mirrokni (VP Google, известный исследователь в области алгоритмов) и коллеги.</p><ul><li>Сжимает KV-кеш до 3 бит на значение (с обычных 16) — уменьшение в ~5 раз</li><li>Не требует дообучения модели — работает с любой LLM «из коробки»</li><li>Не теряет качество на стандартных бенчмарках (LongBench, Needle in a Haystack, RULER)</li><li>Ускоряет вычисления attention до 8 раз на NVIDIA H100</li><li>Работает «на лету» — не нужно заранее обрабатывать данные</li></ul><p><b>Важно:</b> TurboQuant сжимает только KV-кеш при инференсе. Он НЕ сжимает веса модели (это делают GPTQ, AWQ, GGUF) и НЕ влияет на обучение. Это принципиально другая задача.</p><h2>Как работает TurboQuant: PolarQuant и QJL простыми словами</h2><p>TurboQuant работает в два этапа. Если упрощать — это как сжатие фотографии в JPEG, только для математических векторов в памяти нейросети.</p><h3>Шаг 1: PolarQuant — умное сжатие основных данных</h3><p>Обычно данные в нейросети хранятся в декартовых координатах — как «иди 3 блока на восток и 4 блока на север». PolarQuant переводит их в полярные координаты — «иди 5 блоков под углом 37 градусов». Перед конвертацией вектор случайным образом поворачивается — это делает распределение координат более однородным.</p><p>В полярных координатах данные распределены более предсказуемо. Это позволяет применить оптимальный квантизатор (Lloyd-Max) к каждой координате независимо, <b>без дополнительных битов на хранение параметров квантизации</b>. В обычных методах эти «служебные» биты съедают 1–2 бита на число — при 3-битной квантизации это колоссальные накладные расходы.</p><h3>Шаг 2: QJL — коррекция ошибок за 1 бит</h3><p>После PolarQuant остаётся небольшая ошибка. QJL (Quantized Johnson-Lindenstrauss) — алгоритм, который использует математическое преобразование Джонсона–Линденштрауса для сведения остаточной ошибки к <b>нулевому смещению</b>, тратя на это всего 1 дополнительный бит.</p><p>Если PolarQuant — это основной «сжиматель», то QJL — это математический «корректор», который гарантирует, что attention-скоры модели остаются точными. Вместе они дают 3-битное представление, которое <a href="https://arxiv.org/abs/2504.19874">математически доказано</a> как близкое к теоретическому пределу сжатия (с точностью до константы ~2,7).</p><h2>Чем TurboQuant отличается от обычной квантизации</h2><p>Если вы запускаете модели через llama.cpp, Ollama или LM Studio, вы уже используете квантизацию — те самые Q4, Q5, Q8 в названиях файлов. Но это квантизация <b>весов</b> модели. TurboQuant работает с совершенно другой сущностью.</p><p><b>Квантизация весов</b> (GPTQ, AWQ, GGUF) — сжимает параметры модели один раз перед запуском. Чем меньше бит — тем хуже качество. Иногда требует калибровки или дообучения.</p><p><b>TurboQuant</b> — сжимает KV-кеш (промежуточные вычисления) на лету во время работы. При 3,5 битах — нулевая потеря качества. Не требует дообучения вообще.</p><p>Эти подходы <b>комплементарны</b>. Вы можете использовать Q4-квантизированную модель И TurboQuant-сжатый KV-кеш одновременно. Одно не заменяет другое — они сжимают разные части системы.</p><h2>TurboQuant на практике: MacBook, видеокарты и облако</h2><h3>Если у вас MacBook или обычный ПК</h3><p>Энтузиасты уже <a href="https://www.reddit.com/r/LocalLLaMA/comments/1s5kdu0/google_turboquant_running_qwen_locally_on_macair/">запустили Qwen 3.5 9B на обычном MacBook Air M4</a> (16 ГБ) с контекстом <b>20 000 токенов</b> — раньше на таком железе длинные контексты были невозможны. Не MacBook Pro, не Mac Studio — обычный Air, самая дешёвая конфигурация. В Reddit-треде есть видео-демо.</p><p>Это значит: локальная нейросеть на вашем ноутбуке сможет «видеть» в 5 раз больше текста за раз. Длинные документы, полноценные кодовые базы, многостраничные переписки — всё это становится доступнее без покупки нового железа.</p><h3>Если у вас мощная видеокарта</h3><p>Один из разработчиков <a href="https://github.com/ggml-org/llama.cpp/discussions/20969">рассчитал</a> для модели 70B Q4_K_M с 34 ГБ свободной VRAM под KV-кеш:</p><ul><li>Без TurboQuant (FP16 KV-кеш): ~109K токенов контекста</li><li>С Q8 KV-кешем: ~218K токенов</li><li>С TurboQuant TQ3 (3 бита): ~536K токенов</li><li>Полное окно контекста 262K легко помещается на 3× RTX 3090</li></ul><h3>Если вы разрабатываете ИИ-продукты</h3><ul><li>В 5–6 раз меньше GPU-памяти на инференс при длинных контекстах</li><li>До 8× ускорение attention на H100</li><li>Снижение стоимости обслуживания каждого запроса к LLM</li></ul><p>Парадокс Джевонса: когда ресурс становится дешевле — его используют больше, а не меньше. Дешёвый инференс = больше компаний внедряют ИИ = спрос на железо в итоге растёт.</p><h2>TurboQuant в llama.cpp, MLX и PyTorch: кто уже пробует</h2><p>Прошло всего несколько дней с публикации, но Open Source сообщество уже на коне:</p><ul><li><a href="https://github.com/ggml-org/llama.cpp/discussions/20969">llama.cpp</a> — минимум 3 независимых реализации, обсуждение интеграции в основную ветку. Одна уже работает на Apple Silicon через Metal</li><li>MLX (Apple Silicon) — разработчики адаптируют алгоритм для маков</li><li>PyTorch + Triton — появились reference-реализации для CUDA-видеокарт</li><li>C/CUDA — рабочая реализация с 18 из 18 пройденных тестов, MSE совпадает с бумагой</li></ul><p>Пока реализации не оптимизированы. Бенчмарки показывают падение скорости генерации в 3–8 раз по сравнению с обычным Q8 KV-кешем. Причина — неоптимизированная операция поворота вектора. Разработчики уже работают над оптимизацией через преобразование Адамара.</p><h2>Как попробовать TurboQuant прямо сейчас</h2><p>Официального кода от Google нет, но сообщество уже сделало несколько рабочих реализаций. Вот как запустить TurboQuant на своём железе — три варианта в зависимости от платформы.</p><h3>На Mac (Apple Silicon M1–M5)</h3><p>Форк <a href="https://github.com/TheTom/llama-cpp-turboquant">llama-cpp-turboquant</a> от TheTom — самая зрелая реализация с Metal-ядрами для Apple Silicon. Работает на M1, M2, M3, M4, M5.</p><p>Флаг turbo3 включает 3,5-битную квантизацию KV-кеша (сжатие 4,6× vs FP16). На M5 Max 128 ГБ модель Qwen3.5-35B показывает 2747 tok/s на prefill — на уровне стандартного q8_0, при этом KV-кеш занимает в 4,6 раза меньше памяти.</p><h3>На PC с видеокартой NVIDIA или AMD</h3><p>Форк <a href="https://github.com/unixsysdev/llama-turboquant">llama-turboquant</a> поддерживает CUDA и ROCm. Тип кеша здесь называется tq3_0 (вместо turbo3).</p><h3>Python (для исследований и экспериментов)</h3><p><a href="https://github.com/tonbistudio/turboquant-pytorch">turboquant-pytorch</a> — reference-реализация на PyTorch. Подойдёт для изучения алгоритма и проверки сжатия на своих моделях.</p><p>Ollama и LM Studio пока <b>не поддерживают</b> TurboQuant — они используют свои сборки llama.cpp, в которые метод ещё не влит. Но можно поднять llama-server из форка выше и подключиться к нему как к OpenAI-совместимому API.</p><h2>Бенчмарки TurboQuant: результаты из бумаги и от сообщества</h2><p><b>Из бумаги Google</b> (тестировалось на Gemma и Mistral):</p><ul><li>LongBench, Needle in a Haystack, ZeroSCROLLS, RULER, L-Eval — при 3,5 битах «абсолютная нейтральность качества»</li><li>При 2,5 битах — «маргинальная деградация»</li><li>4-bit TurboQuant: ускорение attention до 8× на H100 по сравнению с 32-bit baseline</li><li>Сжатие KV-кеша минимум в 6 раз</li></ul><p><b>Из независимых реализаций:</b></p><ul><li>TQ3 (3 бита): MSE = 0,034, сжатие 4,9× vs FP16</li><li>TQ4 (4 бита): MSE = 0,009, сжатие 3,8× vs FP16</li><li>Размер блока: 52 байта на 128 значений (для TQ3)</li></ul><h2>Почему TurboQuant обвалил акции Micron — и стоит ли переживать</h2><p>Рынок отреагировал панически: Micron −25%, Samsung −4,7%, SK Hynix −6,2%. Но аналитики Morgan Stanley, JPMorgan, Citigroup и Goldman Sachs единодушны — реакция чрезмерна. Вот почему:</p><ol><li><b>TurboQuant не трогает обучение.</b> HBM-память, основной драйвер прибыли чипмейкеров, нужна для обучения моделей. TurboQuant сжимает только инференс</li><li><b>Это исследование, не продукт.</b> Официального кода от Google нет, эксперименты на моделях ~8B параметров</li><li><b>Прецедент DeepSeek.</b> В январе 2025 тоже обвалили чипмейкеров — затем спрос только вырос</li><li><b>Фиксация прибыли.</b> Micron был +300% за год, Samsung +200%. Инвесторы искали повод зафиксировать прибыль</li></ol><blockquote>Если TurboQuant снизит операционные расходы на ИИ до одной шестой от текущего уровня, компании, которые до сих пор не внедряли ИИ из-за высокой стоимости, войдут в ИИ-экосистему.</blockquote><p>Подробнее о реакции рынка — в <a href="https://tproger.ru/news/google-turboquant-obvalil-akcii-proizvoditelej-pamyati-na-25----r">нашей новости</a>.</p><h2>Часто задаваемые вопросы</h2><p><b>Чем TurboQuant отличается от GGUF/GPTQ/AWQ?</b><br />GGUF, GPTQ и AWQ сжимают <i>веса</i> модели (параметры). TurboQuant сжимает <i>KV-кеш</i> — временную рабочую память, которая используется во время инференса. Эти подходы дополняют друг друга.</p><p><b>Можно ли уже использовать TurboQuant?</b><br />Экспериментально — да. Несколько независимых реализаций для llama.cpp и MLX уже работают. Но в основную ветку llama.cpp и в Ollama/LM Studio метод пока не интегрирован. Ожидается в ближайшие недели–месяцы.</p><p><b>Нужно ли переобучать модель для TurboQuant?</b><br />Нет. TurboQuant — data-oblivious метод, который применяется к любой существующей LLM без дообучения и без калибровочных данных.</p><h2>TurboQuant: итоги и выводы</h2><ul><li>TurboQuant — прорыв в сжатии KV-кеша, но не «убийца GPU» и не «конец эпохи дорогого железа»</li><li>Работает <b>вместе</b> с обычной квантизацией весов, а не вместо неё</li><li>Реально позволяет запускать более длинные контексты на том же железе — в 5 раз длиннее</li><li>Сообщество уже пишет реализации для llama.cpp и MLX</li><li>До полноценного внедрения в популярные инструменты — недели или месяцы</li><li>Обвал акций — чрезмерная реакция, аналитики рекомендуют покупать на падении</li></ul><p>Следите за <a href="https://github.com/ggml-org/llama.cpp/discussions/20969">GitHub-дискуссией в llama.cpp</a> — именно там появится рабочая реализация для повседневного использования. А <a href="https://research.google/blog/turboquant-redefining-ai-efficiency-with-extreme-compression/">блог Google Research</a> и <a href="https://arxiv.org/abs/2504.19874">бумага на arXiv</a> — для тех, кто хочет погрузиться в математику.</p>]]></content:encoded>
    </item>
    <item>
      <title>Google TurboQuant обвалил акции производителей памяти на 25% — разбираемся, что произошло</title>
      <link>https://tproger.ru/news/google-turboquant-obvalil-akcii-proizvoditelej-pamyati-na-25----r</link>
      <comments>https://tproger.ru/news/google-turboquant-obvalil-akcii-proizvoditelej-pamyati-na-25----r?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/google-turboquant-obvalil-akcii-proizvoditelej-pamyati-na-25----r</guid>
      <description><![CDATA[<p>Google опубликовал TurboQuant — алгоритм сжатия KV-кеша LLM в 6 раз. Micron упал на 25%, Samsung на 4,7%. Но аналитики называют это «возможностью для покупки». Разбираем почему.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/google-turboquant-obvalil-akcii-proizvoditelej-pamyati-na-25----r">Google TurboQuant обвалил акции производителей памяти на 25% — разбираемся, что произошло</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Новости]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[NVIDIA]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 27 Mar 2026 15:56:39 GMT</pubDate>
      <content:encoded><![CDATA[<p>Google Research опубликовал алгоритм <b>TurboQuant</b>, который сжимает потребление памяти при инференсе LLM <b>в 6 раз</b> и ускоряет вычисления attention <b>в 8 раз</b> — без потери качества. Реакция рынка: акции Micron упали на 25% за неделю, Samsung — на 4,7%, SK Hynix — на 6,2%.</p><p>Но паника оказалась преждевременной. Разбираемся, что на самом деле делает TurboQuant и почему аналитики советуют покупать на падении.</p><p><b>Главное:</b> TurboQuant сжимает только KV-кеш при инференсе, не веса модели и не данные обучения. HBM-память, основной драйвер прибыли чипмейкеров, нужна для обучения — а TurboQuant на обучение не влияет вообще. Аналитики Morgan Stanley, JPMorgan и Citigroup называют обвал «возможностью для покупки».</p><h2>Что такое TurboQuant</h2><p><a href="https://research.google/blog/turboquant-redefining-ai-efficiency-with-extreme-compression/">TurboQuant</a> — алгоритм сжатия <b>KV-кеша</b> (Key-Value cache) при инференсе больших языковых моделей. KV-кеш — это «рабочая память» модели: промежуточные вычисления механизма внимания, которые хранятся в VRAM GPU. Чем длиннее контекст, тем больше KV-кеш занимает места.</p><p>Алгоритм работает в два этапа:</p><ol><li><b>PolarQuant</b> — переводит векторы из декартовых координат в полярные после случайного вращения, экономя 1–2 бита на число</li><li><b>QJL</b> (Quantized Johnson-Lindenstrauss) — коррекция ошибок: сводит остаточную ошибку к одному биту, обеспечивая нулевое смещение attention scores</li></ol><p>Результат: сжатие KV-кеша до <b>3 бит</b> на значение (с 16 стандартных), ускорение до 8x на H100, <b>нулевая потеря качества</b> на бенчмарках. Не требует дообучения. Статья принята на <a href="https://arxiv.org/abs/2504.19874">ICLR 2026</a>.</p><h2>Как отреагировал рынок</h2><ul><li><b>Micron (MU)</b> — с $471 до $345, падение ~25% за неделю</li><li><b>SK Hynix</b> — -6,2% за день</li><li><b>Samsung</b> — -4,7%, 4 дня падения подряд</li><li><b>Kioxia</b> — -6% в Токио</li><li><b>Western Digital, SanDisk</b> — -7%+</li><li><b>KOSPI</b> -3,2%, <b>NASDAQ</b> -2,4%</li></ul><h2>Почему паника преждевременна</h2><p>Аналитики Morgan Stanley, JPMorgan, Citigroup и Goldman Sachs единодушны: реакция чрезмерна. Почему:</p><ol><li><b>TurboQuant сжимает только KV-кеш, не веса и не данные обучения.</b> HBM нужна для обучения — TurboQuant на обучение не влияет</li><li><b>Парадокс Джевонса.</b> Дешевле инференс = больше компаний используют ИИ = спрос растёт</li><li><b>Прецедент DeepSeek.</b> В январе 2025 тоже обвалили чипмейкеров — затем спрос только вырос</li><li><b>Исследование, не продукт.</b> Нет кода, эксперименты на ~8B моделях</li><li><b>Фиксация прибыли.</b> Micron +300%, Samsung +200%, Kioxia +700% за год</li></ol><blockquote>If TurboQuant cuts AI operating costs to one-sixth of current levels, companies that have hesitated to adopt AI due to cost burdens will enter the AI ecosystem.</blockquote><blockquote>Sell-off stemmed from an interpretation error caused by confusing the roles of memory capacity and memory bandwidth.</blockquote><h2>FAQ</h2><h3>TurboQuant убьёт спрос на память?</h3><p>Нет. Алгоритм сжимает только KV-кеш при инференсе. HBM для обучения не затронута. Глава SK Group заявил, что дефицит чипов памяти сохранится до 2030 года.</p><h3>Можно ли уже использовать TurboQuant?</h3><p>Официального кода от Google нет, но уже появились независимые реализации на PyTorch+Triton, MLX и C/CUDA для llama.cpp.</p><h3>Чем это отличается от обычной квантизации?</h3><p>GPTQ/AWQ сжимают веса модели с потерей качества. TurboQuant сжимает KV-кеш (временные вычисления) с математически доказанным нулевым смещением. Подходы комплементарны.</p><p>Источники: <a href="https://research.google/blog/turboquant-redefining-ai-efficiency-with-extreme-compression/">Google Research</a>, <a href="https://www.cnbc.com/2026/03/26/google-ai-turboquant-memory-chip-stocks-samsung-micron.html">CNBC</a>, <a href="https://en.sedaily.com/finance/2026/03/27/googles-turboquant-paper-rattles-samsung-sk-hynix-memory">Seoul Economic Daily</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Headless WordPress: архитектура с Next.js, GraphQL и Cloudflare</title>
      <link>https://tproger.ru/articles/headless-wordpress--arhitektura-s-next-js--graphql-i-cloudflare</link>
      <comments>https://tproger.ru/articles/headless-wordpress--arhitektura-s-next-js--graphql-i-cloudflare?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Александр Пехота]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/headless-wordpress--arhitektura-s-next-js--graphql-i-cloudflare</guid>
      <description><![CDATA[<p>Разбираем headless WordPress на практике: Next.js, Cloudflare Workers, GraphQL и архитектура быстрых и масштабируемых сайтов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/headless-wordpress--arhitektura-s-next-js--graphql-i-cloudflare">Headless WordPress: архитектура с Next.js, GraphQL и Cloudflare</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[WordPress]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Cloudflare]]></category>
      <category><![CDATA[CMS]]></category>
      <category><![CDATA[CDN]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 26 Mar 2026 11:29:45 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Почему WordPress?</h2><p>WordPress часто не любят backend-разработчики, и у каждого на это есть свои причины. Кому-то не нравится функциональный стиль разработки, кто-то критикует form-builder и экосистему плагинов. У других WordPress как CMS и PHP как язык программирования до сих пор ассоциируются со стереотипами 10–15-летней давности - будто они устарели и уступают современным технологиям.</p><p>При этом реальность такова, что и PHP, и WordPress - отличные и современные инструменты, которые очень хорошо выполняют свои задачи. Опустим PHP - статья не об этом. Что же можно сказать про WordPress как про продукт и как CMS?</p><p>WordPress по‑прежнему остаётся самой популярной системой управления контентом. Согласно данным команды WordPress, платформа обслуживает более 43% всех веб-сайтов и занимает долю в 61% на рынке CMS. Также статистика показывает, что WordPress используется примерно на 59% сайтов, где известна CMS (это около 42% всего веба).</p><p>Данные были взяты из официального блога WordPress и сайта w3techs.com:</p><ul><li><a href="https://wordpress.com/blog/2025/04/17/wordpress-market-share/" rel="nofollow">https://wordpress.com/blog/2025/04/17/wordpress-market-share/ </a></li><li><a href="https://w3techs.com/technologies/overview/content_management" rel="nofollow">https://w3techs.com/technologies/overview/content_management</a></li><li><a href="https://w3techs.com/technologies/details/cm-wordpress" rel="nofollow">https://w3techs.com/technologies/details/cm-wordpress</a></li></ul><p>При этом, традиционный WordPress объединяет CMS, шаблоны на PHP и монолитные темы. Такая связка усложняет разработку с использованием современных JS фреймворков, а также затрудняет независимое масштабирование фронтенда и бэкенда, и оптимизацию производительности и безопасности. Жёсткая связка страниц, устаревшие PHP‑функции и не самый удобный девелоперский опыт часто заставляют команды искать альтернативы.</p><p>Headless WordPress решает эти проблемы: CMS становится админ-панелью для управления контентом, а отдельный фронтенд отвечает за UI. Такое разделение обязанностей даёт несколько преимуществ: четкое разделение ответственности, независимое масштабирование интерфейса и CMS, упрощенную локальную разработку и CI/CD. CMS превращается в API‑ориентированное хранилище, а современные фреймворки вроде Next.js берут на себя маршрутизацию и рендеринг.</p><h2>Headless WordPress с использованием WPGraphQL</h2><p>Чтобы использовать WordPress как headless‑CMS, нужен API. Также есть интересный пост про headless wordpress в их <a href="https://wordpress.com/blog/2025/03/20/headless-wordpress/" rel="nofollow">официальном блоге</a>.</p><p>В WordPress из коробки есть REST API, но для frontend и mobile приложений часто удобнее использовать GraphQL. <a href="https://wordpress.org/plugins/wp-graphql/" rel="nofollow">WPGraphQL</a> - это open source плагин, который добавляет GraphQL API в WordPress. Используя WPGraphQL, мы получаем:</p><ul><li>Гибкие запросы к таким сущностям, как посты, страницы, произвольным типам постов, таксономиям и пользователям.</li><li>Систему расширений которая позволяет расширять функционал GraphQL бекенда и таким образом поддерживать популярные плагины, тем самым позволяя возвращать дополнительные поля которые не относятся к стандартным полям Wordpress.</li><li>GraphQL API, который даёт очень удобный формат для интеграции фронтенд фреймворков таких как Next.js, Astro и SvelteKit.</li><li>Оптимизацию производительности, поскольку клиент запрашивает только нужные поля и данные делая один запрос вместо группы REST запросов + отдельный фронтенд забирает на себя часть запросов.</li></ul><p>В дополнение к доступному функционалу WPGraphQL можно добавлять дополнительные плагины-расширения, такие как <a href="https://wordpress.org/plugins/add-wpgraphql-seo/" rel="nofollow">WPGraphQL Yoast SEO</a> и <a href="https://woographql.com/" rel="nofollow">WooGraphQL</a> (WPGraphQL для WooCommerce). Таким образом добавив несколько плагинов в базовую инсталляцию CMS можно из коробки получить полностью функциональный GraphQL бекенд, который может покрыть запросы для блога, сео функционал, онлайн-магазин и тд.</p><p>Важно отметить, что изначальная идея использовать WPGraphQL пришла из статьи в блоге <a href="https://vercel.com/kb/guide/wordpress-with-vercel" rel="nofollow">Vercel</a>.</p><figure><img src="https://media.tproger.ru/user-uploads/136948/2026-03-25/838bc085-bfe5-4888-a326-6dcc00b0aaf5.webp" alt="Сравнение традиционного WordPress и headless WordPress: монолитная CMS с PHP темами против архитектуры с WPGraphQL API и Next.js фронтендом" /><figcaption>Традиционный WordPress vs Headless WordPress: разделение CMS и frontend через API (WPGraphQL + Next.js)</figcaption></figure><h2>Фронтенд на Next.js</h2><p><a href="https://nextjs.org/" rel="nofollow"> Next.js</a> - production-ready React-фреймворк, который разрабатывается компанией Vercel. Многие используют его по умолчанию для разных headless‑проектов. Наш пример headless-wordpress не исключение. Фреймворк предлагает удобную <a href="https://nextjs.org/docs/pages/building-your-application/routing" rel="nofollow">маршрутизацию</a> на базе файловой системы, где любой файл в папке pages автоматически становится маршрутом и поддерживает несколько стратегий рендеринга:</p><ul><li>Server‑side rendering (SSR) позволяет генерировать HTML при каждом запросе.</li><li>Статическая генерация (включая [Incremental Static Regeneration])</li><li>React Server Components, стратегия которая дает гибкость в балансировании производительности и кэширования.</li></ul><p>Это делает Next.js хорошей платформой для работы с GraphQL API и рендеринга страниц React‑компонентами. Если у вас нет опыта с <a href="http://nex.js">Next.js</a> и React, то это не повод не попробовать набросать POC в свободное время. Современные <a href="http://next.js">Next.js</a> и React templates + хороший AI agent помогут адаптировать UI под GraphQL для вас.</p><h2>Почему Cloudflare?</h2><p>Vercel очень часто является платформой по умолчанию для Next.js‑приложений. Более того Next.js адаптирован для запуска из коробки на серверах Vercel. При этом нужно добавить, что идея этой статьи не в том чтобы как-то компрометировать Vercel. Что же нужно знать про Cloudflare чтобы обратить внимание на этот сервис с точки зрения альтернативы для хостинга Next.js?</p><p>Согласно <a href="https://w3techs.com/technologies/details/cn-cloudflare" rel="nofollow">статистике</a>, реверс-прокси сервисы Cloudflare используются примерно на 21,9% всех сайтов в интернете, а это более 82% сайтов, где используется прокси‑сервисы в принципе. Такая распространённость говорит о масштабе, надежности и глобальном охвате сервиса. Но Cloudflare - это не только reverse-proxy. Компания разрабатывает целую группу облачных сервисов, включая такие сервисы, как Cloudflare Pages - альтернатива Github Pages, Workers - Serverless functions (по аналогии с AWS Lambda), Контейнеры, Очереди, AI сервисы, R2 Object Storage, и другие. В дополнение ко всему, компания предоставляет такие сервисы, как защита сайта (site-protection), VPN и капча (human-detection captcha). Такое разнообразие сервисов делает сервис очень распространенным.</p><h2>Лимиты бесплатного тарифа Cloudflare</h2><p>Одним из самых интересных аргументов в пользу Cloudflare можно считать их бесплатный тариф. Защита от DDoS, Universal SSL и глобальную CDN доступны бесплатно. Также бесплатный тариф включает большинство из вышеперечисленных облачных сервисов. Например, Cloudflare Workers, который можно использовать для хостинга Next.js-проектов, бесплатно даёт 100,000 запросов в день. Или R2 object storage - альтернатива S3 по умолчанию дает 10GB пространства, которое можно использовать для хранения статики или других данных. Этого более чем достаточно чтобы поэкспериментировать на выходных с новым стеком и вполне достаточно для того, чтобы бесплатно хостить ваш проект до тех пор пока у вас не пойдет серьезный трафик. Ниже приведена таблица с некоторыми из Cloudflare сервисов и что включено в бесплатный тариф.</p><figure><img src="https://media.tproger.ru/user-uploads/136948/2026-03-25/826d92c6-cecd-4d70-a2ee-78733a324a70.webp" alt="Таблица сервисов Cloudflare (Workers, KV, D1, R2 и др.) с лимитами бесплатного тарифа и их назначением" /><figcaption>Cloudflare free tier: сервисы и лимиты, достаточные для запуска headless WordPress + Next.js проекта. Взято с https://dev.to/ioniacob/which-cloudflare-services-are-free-2025-free-tier-guide-53jl.</figcaption></figure><h2>Запуск Serverless функций на edge-серверах</h2><p>Cloudflare Workers позволяют запускать серверлесс‑код по всей сети Cloudflare. Ниже приведено изображение показывающее как работает Edge CDN, когда например статика продублирована на все доступные сервера и таким образом пользователь получает ресурсы с самого близлежащего сервера.</p><figure><img src="https://media.tproger.ru/user-uploads/136948/2026-03-25/6c12d101-f282-4d1a-8d7a-b4119fabdfda.webp" alt="Схема работы CDN: пользователи обращаются к ближайшим edge-серверам, которые кешируют контент и уменьшают нагрузку на origin-сервер" /><figcaption>Как работает CDN: пользователь получает контент с ближайшего edge-сервера, снижая задержку и нагрузку на origin. Источник: https://www.cloudflare.com/learning/cdn/what-is-a-cdn/</figcaption></figure><p>Эта картинка хороша тем, что аналогично CDN статике на этих же серверах можно запускать и Workers (Lambda) функции, тем самым ускоряя вашу инфраструктуру еще больше.</p><p>Одной из интересных особенностей Workers-функций является отсутствие cold-starts. Любой cloud provider обычно подымает docker container или виртуализированное окружение в момент первого запуска программы, а это всегда задержка. Минусом Workers-функций является тот факт что их Runtime API требует чтобы код мог использовать их Web platform APIs. А это в свою очередь ограничивает выбор языка программирования: Javascript, Typescript и WebAssembly. Но благодаря такому подходу Workers используют изолированную модель запуска  и могут быть прогреты еще до момента запуска кода этого воркера. Прогрев начинается еще на этапе TLS-соединения между клиентом и серверами Cloudflare. Полный текст статьи можно почитать в их <a href="https://blog.cloudflare.com/eliminating-cold-starts-with-cloudflare-workers/" rel="nofollow">блоге</a>.</p><figure><img src="https://media.tproger.ru/user-uploads/136948/2026-03-25/4bcd2a0a-5161-46f5-984c-87f4097411dc.webp" alt="Схема работы Cloudflare Workers: прогрев (warmup) и загрузка воркера происходят во время TLS handshake до выполнения HTTP-запроса" /><figcaption>Как Cloudflare Workers устраняют cold-start: прогрев воркера происходит ещё на этапе TLS-соединения.</figcaption></figure><p>Воркеры выполняются на edge-серверах, которые находятся ближе всего к пользователям, тем самым уменьшая задержку и разгружая origin‑сервер (в нашем случае WordPress backend). По аналогии с другими cloud-провайдерами, код внутри Workers Runtime может использовать другие сервисы Cloudflare, такие как:</p><ul><li><a href="https://developers.cloudflare.com/workers/runtime-apis/cache/" rel="nofollow">Cache API</a> - позволяет читать и записывать данные в глобальный edge‑кэш через caches.default, что удобно для кэширования GraphQL‑ответов или страниц Next.js.</li><li><a href="https://developers.cloudflare.com/kv/" rel="nofollow">Workers KV</a> - распределенное key‑value‑хранилище для конфигурации и небольших наборов данных; можно хранить и получать данные глобально с низкой задержкой.</li><li><a href="https://developers.cloudflare.com/workers/configuration/cron-triggers/" rel="nofollow">Cron Triggers</a> - можно сопоставить cron‑выражение с обработчиком scheduled(), чтобы запускать периодические задачи, например, уборку кэша или обновление данных. Триггеры выполняются на малоиспользуемых машинах, максимизируя эффективность.</li></ul><p>Наличие доступа к дополнительным сервисам, таким как базы данных (D1), объектное хранилище (R2), очереди и AI даёт свободу строить более сложные и гибкие архитектуры, что очень полезно в дальнейшем на больших масштабах.</p><h2>OpenNext: мост между Next.js и Cloudflare</h2><p>Самостоятельный деплой Next.js на разные платформы непрост, поскольку среда исполнения Vercel отличается от других. Можно поднять Next.js на Node‑сервере, но его работа отличается от edge‑режима Vercel. OpenNext - это проект с открытым исходным кодом, который адаптирует Next.js для разных серверлесс‑платформ. Важно сказать что у Next.js нет нативного способа само разворачивания на других платформах, кроме Vercel; существующие отдельные адаптеры разрознены и сложны в поддержке. <a href="https://opennext.js.org/" rel="nofollow">OpenNext</a> объединяет усилия в одном адаптере, переводя выход сборки Next.js в формат, совместимый с основными облачными платформами. Проект поддерживают сообщество SST (AWS), команда Cloudflare и Netlify. Соответственно, с помощью OpenNext можно развернуть Next.js на Cloudflare Workers, сохраняя SSR, статическую генерацию и API‑маршруты.</p><p>Cloudflare‑адаптер устанавливается через @opennextjs/cloudflare. Далее следует установить<a href="https://developers.cloudflare.com/workers/wrangler/"> Wrangler</a>, настроить wrangler.toml с вашим Account ID и создать open-next.config.ts для управления кэшем и ассетами. Адаптер собирает приложение Next.js под среду Cloudflare, создает edge‑воркер и конфигурирует кэш для статики и ISR‑страниц (например, используя R2). После публикации Git‑интеграция Cloudflare автоматически разворачивает приложение при каждом пуше в GitHub или GitLab, а для pull‑request создает превью.</p><figure><img src="https://media.tproger.ru/user-uploads/136948/2026-03-25/ffd3d8f8-9e94-4ee2-b1b8-7b7d075a9bbd.webp" alt="Логотипы OpenNext, Cloudflare, AWS Amplify и Netlify, показывающие поддержку деплоя Next.js на разные cloud-платформы" /><figcaption>OpenNext как единый адаптер для деплоя Next.js приложений на разные платформы: Cloudflare, AWS и Netlify</figcaption></figure><h2>Кэширование и уровни производительности</h2><p>Архитектура headless WordPress + Next.js + Cloudflare обычно включает несколько уровней кэша:</p><ol><li>Кэш браузера - стандартный HTTP‑кэш на стороне клиента.</li><li>Кэш edge‑рантайма - Cache API Cloudflare Workers сохраняет HTML‑страницы или GraphQL‑ответы рядом с пользователем; при попадании в кэш контент отдаётся мгновенно, а промахи идут к воркеру или origin.</li><li>Кэш ISR Next.js - технология Incremental Static Regeneration сохраняет отрендеренные страницы на сервере и обновляет их по запросу, снижая нагрузку на WordPress API.</li><li>Кэш GraphQL - API WPGraphQL может реализовывать кэширование по времени или тегам (например, через WPGraphQL Smart Cache), чтобы управлять сроком жизни ответов.</li></ol><p>Эта многоуровневая иерархия кэша обеспечивает, что большинство запросов вообще не доходят до вашего WordPress‑сервера, повышая производительность и снижая нагрузку.</p><p>Пример конечной архитектуры показан на изображении ниже.</p><figure><img src="https://media.tproger.ru/user-uploads/136948/2026-03-25/37141150-e160-423b-a8cd-3784117e6499.webp" alt="" /><figcaption>Архитектура headless WordPress + Next.js на Cloudflare: edge-рендеринг, многоуровневый кэш и взаимодействие с WPGraphQL</figcaption></figure><h2>Автоматизация периодических задач</h2><p>Headless‑сайтам часто требуются периодические действия - например, обновление кэша ISR или синхронизация данных. Cron Triggers Cloudflare позволяют планировать запуск воркера по cron‑выражению. Обработчик scheduled() срабатывает по расписанию и подходит для обслуживания и получения сторонних данных. Триггеры выполняются на малоиспользуемых машинах по всему миру и легко управляются через Wrangler или панель Cloudflare.</p><h2>Модернизация PHP‑стека с Roots toolkit</h2><p>Хотя headless WordPress переносит рендеринг на JavaScript, CMS всё ещё нужно поддерживать. В качестве бонуса хочется порекомендовать экосистему <a href="https://roots.io/" rel="nofollow">Roots</a>, которая предлагает современный инструментарий для разработки на WordPress:</p><figure><img src="https://media.tproger.ru/user-uploads/136948/2026-03-25/27e070e5-39c8-425b-8142-b0b2ce0af69d.webp" alt="Скриншот сайта Roots с описанием инструментов для разработки WordPress: Bedrock, Sage, Trellis и Acorn" /><figcaption>Roots — современный инструментарй для разработки WordPress с использованием Composer, Blade и автоматизированного деплоя. Источник: Roots - https://roots.io/</figcaption></figure><ul><li><a href="https://roots.io/bedrock/" rel="nofollow">Bedrock</a> - шаблон WordPress, которая устанавливает ядро, плагины и темы через Composer. Таким образом Bedrock дает современную для PHP проектов структуру проекта, улучшает структуру папок, использует концепты Двенадцать факторов для конфигурации приложения с помощью .env‑файлы и тд. Более того, управление зависимостями через Composer повышает надежность и позволяет делать деплой приложения на разные сервера без страха что-то забыть или упустить.</li><li><a href="https://roots.io/sage/" rel="nofollow">Sage</a> - стартовая тема WordPress, использующая Blade от Laravel для шаблонов и интегрирующая Tailwind CSS. Sage автоматически генерирует theme.json из конфигурации Tailwind, поддерживает live preview блокового редактора с Vite и позволяет создавать компоненты на Blade. Это помогает фронтенд‑разработчикам отойти от устаревших подходов для разработки тем Wordpress с нуля.</li><li><a href="https://roots.io/trellis/" rel="nofollow">Trellis</a> - DevOps‑инструмент на базе Ansible, который поднимает серверы и автоматизирует деплой. Trellis предоставляет LEMP‑стек (Ubuntu 24.04, Nginx, PHP 8.3, MariaDB), выполняет деплой без downtimes и из коробки поддерживает SSL‑сертификаты. CLI помогает создавать и настраивать серверы, а также разворачивать проекты с атомарными релизами и откатами.</li><li><a href="https://roots.io/acorn/" rel="nofollow">Acorn</a> - интеграция, позволяющая использовать функционал Laravel в WordPress. С Acorn становятся доступны такие инструменты как Blade‑шаблоны, миграции, роутинг, кэширование и Artisan‑подобный CLI внутри WordPress. Это позволяет разработчикам строить плагины и фичи WordPress с использованием современных PHP‑подходов и современного фреймворка .</li></ul><p>Эти инструменты показывают, что экосистема WordPress продолжает развиваться и хорошо сочетается с современными подходами. Иными словами, WordPress - отличное решение, если знать, как его правильно готовить.</p><h2>Собираем всё вместе</h2><p>Архитектура приложения headless WordPress + Next.js + Cloudflare выглядит приблизительно так:</p><ol><li>WordPress (headless) - работает на традиционном сервере или в контейнере. Редакторы управляют контентом в админке. WPGraphQL и его расширения предоставляют GraphQL‑endpoint с данными, SEO и другой информацией, например данными о магазине.</li><li>Next.js фронтенд - React‑приложение, которое получает данные через GraphQL, рендерит страницы на сервере (SSR) или статически (ISR/SSG) и обрабатывает маршрутизацию и взаимодействие с клиентом. Код хранится в Git и автоматически разворачивается благодаря Git‑интеграции Cloudflare.</li><li>Cloudflare Workers - размещают приложение Next.js на edge через OpenNext. Workers исключают cold-starts и работают по аналогии с CDN как можно ближе к пользователю. Они также выполняют кэширование, обрабатывают API‑маршруты и запускают cron‑задачи.</li><li>Кэш на edge и в браузере - несколько уровней кэша гарантируют быструю отдачу статики и отрендеренных страниц. KV, R2 или D1 могут хранить дополнительные данные вроде сессий или объектов.</li></ol><h2>Заключение</h2><p>Headless‑архитектура объединяет универсальность WordPress и гибкость современных JavaScript‑фреймворков. Экспонируя контент через WPGraphQL и потребляя его в Next.js, можно получить больше контроля над рендерингом и тем самым улучшить производительность. Размещение фронтенда на Cloudflare Workers через OpenNext позволяет приблизить фронтенд код к пользователям, устраняет cold-starts, позволяет использовать free-tier и продвинутые уровни кэширования. Инструменты вроде Bedrock, Sage, Trellis и Acorn модернизируют PHP/Wordpress‑сторону и делают CMS такой же удобной в работе, как и современный Next.js/React-фронтенд. Вместе эти технологии создают мощный стек для создания быстрых, масштабируемых и безопасных сайтов, будь то хакатон, pet‑проект или серьёзный продакшн.</p>]]></content:encoded>
    </item>
    <item>
      <title>Datadog уменьшила размер Go-бинарников на 77% без потери функций. Как им это удалось?</title>
      <link>https://tproger.ru/news/datadog-umenwila-razmer-go-binarnikov-na-77--bez-poteri-funkcij</link>
      <comments>https://tproger.ru/news/datadog-umenwila-razmer-go-binarnikov-na-77--bez-poteri-funkcij?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/datadog-umenwila-razmer-go-binarnikov-na-77--bez-poteri-funkcij</guid>
      <description><![CDATA[<p>Datadog сократила Go-бинарники на 77%: аудит зависимостей, включение оптимизаций линкера и удаление plugin дали минус сотни мегабайт</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/datadog-umenwila-razmer-go-binarnikov-na-77--bez-poteri-funkcij">Datadog уменьшила размер Go-бинарников на 77% без потери функций. Как им это удалось?</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 25 Feb 2026 04:53:36 GMT</pubDate>
      <content:encoded><![CDATA[<p>За пять лет размер артефактов Datadog Agent вырос с 428 МБ до 1,22 ГБ.</p><p>Новые фичи, интеграции, поддержка Kubernetes и облаков сделали продукт мощнее... и заметно тяжелее. Это стало проблемой для serverless, IoT и контейнеров.</p><p>Вместо того чтобы урезать функциональность, команда решила «переломить кривую роста». За полгода (v7.60.0 → v7.68.0) им удалось сократить размер Go-бинарников до 77%.</p><h2>Аудит зависимостей: минус 36 МБ одной правкой</h2><p>Первый шаг — полный разбор зависимостей. С помощью go list, goda и go-size-analyzer разработчики искали пакеты, которые подтягиваются в бинарник случайно.</p><p>В одном случае trace-agent тащил 526 пакетов Kubernetes из-за одной функции, которая фактически не использовала k8s-код. Перенос ее в отдельный пакет позволил компилятору выбросить все лишнее — минус 36 МБ.</p><p>И таких случаев нашли десятки.</p><h2>Разблокировка «скрытой» оптимизации линкера</h2><p>Вторая находка дала еще ~20% выигрыша. В Go есть оптимизация method dead code elimination, но она отключается, если используется reflect.MethodByName с динамическими именами (например, в text/template).</p><p>Команда нашла проблемные вызовы через -dumpdep и утилиту <i>whydeadcode</i>, пропатчила зависимости и даже форкнула text/template, чтобы отключить динамические вызовы методов. Результат — еще минус около 100 МБ.</p><h2>Удаление plugin = минус 245 МБ</h2><p>Самый неожиданный эффект дал пакет plugin. Его импорт заставлял линкер сохранять все методы типов, отключая оптимизацию.</p><p>Выяснилось, что зависимость приходила через <i>containerd</i>, хотя агент ее не использовал. После добавления build-тега и обновления зависимостей, основной Linux amd64-бинарник «похудел» на 245 МБ.</p><h2>Итог</h2><ul><li>Security Agent: −77%</li><li>Process Agent: −74%</li><li>Trace Agent: −74%</li><li>Общий .deb: 1,22 ГБ → 688 МБ</li></ul><p>И главное — все это <b>без удаления функций</b>. Только системная чистка зависимостей, грамотные build-теги и возвращение оптимизаций линкера.</p>]]></content:encoded>
    </item>
    <item>
      <title>Российские ИИ-инструменты, которые советуем затестить в 2026</title>
      <link>https://tproger.ru/articles/rossijskie-ii-instrumenty--kotorye-sovetuem-zatestit-v-2026</link>
      <comments>https://tproger.ru/articles/rossijskie-ii-instrumenty--kotorye-sovetuem-zatestit-v-2026?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Валерия Турчак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/rossijskie-ii-instrumenty--kotorye-sovetuem-zatestit-v-2026</guid>
      <description><![CDATA[<p>Мы собрали пять инструментов, которые решают конкретные задачи: от генерации презентаций до работы с кодом и аналитикой. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/rossijskie-ii-instrumenty--kotorye-sovetuem-zatestit-v-2026">Российские ИИ-инструменты, которые советуем затестить в 2026</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 21 Jan 2026 11:40:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>В 2026 году российский рынок ИИ-инструментов перешёл из стадии экспериментов в рабочие решения. Это не значит, что они лучше или хуже западных аналогов. Просто у них другая логика: данные остаются в России, интерфейс полностью на русском, оплата через российские карты, а техподдержка отвечает по московскому времени.</p><p>Мы собрали пять инструментов, которые решают конкретные задачи: от генерации презентаций до работы с кодом и аналитикой. Их стоит протестировать, если вы ещё этого не сделали. Давайте разбираться, что каждый из них умеет делать и для кого они вообще созданы.</p><h2>1. Сократик — генерация презентаций через нейросеть</h2><p><a href="https://sokratic.ru/ru">Сократик</a> — веб-сервис, который собирает презентации автоматически. Вы загружаете текст, документ или таблицу, задаёте тему и параметры — ИИ формирует структуру, пишет текст для слайдов, подбирает изображения и оформляет всё в выбранном стиле.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/e472907b-ed44-42dc-91a2-7b53fc889642.jpg" alt="" /></figure><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/2efef8f6-3877-4c17-83e2-cc9b31864e45.jpg" alt="" /></figure><p>Под капотом работают LLM-модели для текста и диффузионные модели для генерации графики. Сервис умеет анализировать загруженные данные: парсит таблицы, вытаскивает ключевые тезисы из документов, даже подтягивает информацию из других презентаций.</p><h3>Для кого и какие сценарии</h3><p>Изначально Сократик ориентировался на образование — студентов, школьников, преподавателей, которым нужно быстро структурировать материал и упаковать его в визуал. Но на практике сервис зашёл и в корпоративный сегмент.</p><p>Маркетологи используют его для питчей и концепций: загружаешь бриф, получаешь структуру за пару минут, дорабатываешь вручную. Аналитики и менеджеры собирают отчёты для руководства — Сократик автоматически строит графики и инфографику по данным из таблиц. В госучреждениях и бизнесе сервис помогает быстро упаковывать сложную информацию в понятные слайды без дизайнеров.</p><h3>Как это работает</h3><p>Процесс состоит из трёх этапов: настройка параметров, редактирование структуры и генерация финальной презентации.</p><p>На первом этапе вы выбираете количество слайдов, язык (русский, английский, немецкий, французский, испанский, польский, казахский, узбекский), аудиторию и тональность. Можно загрузить исходники — текстовые документы, PDF, таблицы, картинки или другие презентации в PPTX. Сервис их распарсит и использует как базу для контента.</p><p>Второй этап — редактирование структуры. Это фишка Сократика: план презентации можно корректировать ещё до генерации. Видите структуру, понимаете, что не хватает раздела или слайд лишний — правите прямо здесь. Можно задать общий промпт на всю презентацию или уточнить инструкции для отдельных слайдов.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/d17a4187-ab46-4038-aac0-df64c46c9157.jpg" alt="" /></figure><p>Третий этап — генерация. Занимает 1–2 минуты. Сервис создаёт не только текст и картинки, но и таблицы, графики, инфографику. Всё автоматически подстраивается под выбранный дизайн.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/633aae98-6151-442b-9894-4c8029852bec.jpg" alt="" /></figure><p>После генерации открывается встроенный редактор, где можно вручную доработать слайды: поменять текст, переместить блоки, заменить изображения. Редактор сочетает привычные функции презентационных инструментов с ИИ-правками — например, можно попросить переписать текст слайда или добавить визуал.</p><p>По наблюдениям, весь процесс от настройки до готовой презентации занимает 8–10 минут, если вы проверяете структуру и дорабатываете детали. Если генерировать на автомате без правок — уложитесь в 3 минуты.</p><h3>Что отличает от аналогов</h3><p><b>Главное отличие</b> — возможность редактировать структуру до генерации. В большинстве подобных сервисов вы получаете готовый результат и начинаете его переделывать. Здесь вы видите план, корректируете его и только потом запускаете генерацию.</p><p><b>Второй момент</b> — автоматическая генерация таблиц и графиков. Загружаешь данные из Excel, указываешь, что нужно визуализировать, — Сократик строит диаграммы и вставляет их на слайды.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/b6db88c6-ecdf-4f71-af12-b3c712d07ffb.jpg" alt="" /></figure><p><b>Третья особенность</b> — текст для выступления. Сервис генерирует спикерские заметки на основе готовой презентации.</p><h3>Технические детали</h3><p>Платформа работает полностью в браузере, не требует установки или настройки окружения. Развёрнута в российском облачном контуре, все данные хранятся на локальных серверах.</p><p>Сервис поддерживает импорт разных форматов: текстовые файлы, PDF, PPTX, таблицы (XLS, XLSX), изображения. Экспорт — в PDF или PPTX. Водяной знак на бесплатном тарифе, но его можно убрать за 149 рублей за презентацию.</p><p>Интеграций с внешними платформами нет — Сократик работает автономно. API пока не предоставляется, только веб-интерфейс.</p><p>В плане промптов: есть готовые шаблоны для разных типов презентаций (учебные, бизнес, питчи), можно задавать свои инструкции и сохранять удачные формулировки. Это позволяет выстроить личную библиотеку промптов под типовые задачи.</p><h3>Тарифы и как начать</h3><p><b>Модель freemium:</b> базовые функции бесплатны. Вы можете сгенерировать презентацию, отредактировать её и показать онлайн. Для скачивания без водяного знака нужна оплата.</p><p><b>Есть два варианта:</b> разовая покупка за 149 рублей (скачиваете одну презентацию) или подписка «Безлимит» — 469 рублей в месяц или 2490 рублей в год. Подписка снимает лимиты на скачивания и убирает водяной знак.</p><p>Пробного периода в классическом понимании нет, но фактически вы можете зайти на сайт, создать презентацию, посмотреть результат и оценить качество до оплаты. Платите только тогда, когда нужно скачать файл.</p><p>Поддержка доступна через чат и почту, документация есть на сайте. В Telegram-канале публикуют разборы функций, подсказки и примеры использования.</p><h2>2. Войси — транскрибация аудио и видео в Telegram-боте</h2><p><a href="https://voicee.ru/">Войси — Telegram-бот</a> для транскрибации речи из аудио и видео в текст. Загружаете файл, выбираете функцию — бот за пару минут выдаёт расшифровку с метками времени и подписями спикеров. Включён в реестр российского ПО, работает на локальных серверах, данные не уходят за рубеж.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-21/a01dfb3f-b8f5-4d89-9bd1-6a794ef392c8.png" alt="" /></figure><p>Сервис разбирает разговоры с несколькими голосами, даже если они накладываются друг на друга или есть акценты. Умеет в мультиязычность — если в записи говорят на разных языках, текст сохраняет эти языки. Модели недавно обновили: лучше разбивают абзацы по смыслу, распознают голоса на коротких записях.</p><h3>Основные сценарии для разработчиков и команд</h3><p>В разработке Войси заходит как инструмент для фиксации всех разговоров и переботки видео в текст. Записали созвон с клиентом, загрузили в бота — получили точный протокол с таймкодами. Дальше можно выбрать формат текста для анализа или доработки.</p><p>В блогинге и подкастинге Войси превращает голосовые заметки в готовые посты или статьи. Короткая запись — короткий пост для соцсетей. Длинное интервью — развёрнутая статья для сайта с автоматическим форматированием.</p><h3>Полный набор функций на практике</h3><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-21/fceecfc8-06b7-4a79-bfac-6e6b763684fc.png" alt="" /></figure><h4>Транскрибация</h4><p>Базовая расшифровка с разделением по спикерам и таймкодами. Поддерживает 55 языков.</p><h4>Краткое содержание</h4><p>ИИ находит главное в записи, структурирует в список тезисов. Полезно для встреч, где полчаса болтовни, а суть в трёх пунктах.</p><h4>Субтитры</h4><p>Генерирует SRT-файлы с переводом на 55 языков. Вставляете в видеоредактор — субтитры готовы. Есть LRC для аудиокниг и подкастов с синхронизацией текста по времени.</p><h4>Книжный стиль</h4><p>Очищает текст от "эээ", повторов, разговорных оборотов. Получается текст, готовый к публикации.</p><h4>Перевод</h4><p>С 54 языков на русский в литературном формате. Не дословно, а с сохранением смысла.</p><h4>Пост для блога</h4><p>Из аудио делает короткий пост или полную статью. Короткие голосовые — для Telegram, длинные подкасты — для Tproger или VC.</p><h4>Викторина</h4><p>Генерирует тесты по содержимому записи. Из лекции — квиз для студентов, из фильма — развлекательная викторина.</p><p>После обработки бот предлагает интерактивную веб-версию: синхронный плеер с текстом, редактор, эмоциональная окраска речи. Можно подписать спикеров вручную, убрать таймкоды, скачать чистый текст в PDF/DOCX/TXT. Редактирование сохраняется, доступно по паролю.</p><h4>Технические детали и ограничения</h4><p>Работает только через Telegram-бот, веб-версии для редактирования хватает. Файлы до нескольких часов обрабатываются за минуты (10 часов быстрой обработки в месяц на тарифе). Поддерживает все популярные форматы аудио/видео.</p><p><b>Интеграции:</b> есть API для транскрибации, саммаризации и перевода — документация на voicee.ru/api-transkribacii. Плюс готовые связки с amoCRM для анализа звонков. Разработчики могут встраивать в свои пайплайны.</p><h2>Тарифы и как начать</h2><p>Оплата по балансу по времени обработки. Пополняете — используете, бот сам считает, сколько нужно.</p><ul><li>Войси Лайт: 990 ₽/мес — 10 часов быстрой обработки + безлимит в очереди.</li><li>Полная версия: почасовая, скидки для студентов/пенсионеров (250 ₽/час), бонусы за рефералов.</li></ul><p>Баланс не сгорает. Есть YouTube-канал с примерами (@voicee_ru) и Telegram-канал (@voice_of_voicee) с разборками. Поддержка через @Voicee_Buddy.</p><h2>3. GigaCode — ИИ-ассистент разработчика с агентным режимом</h2><p><a href="http://gitverse.ru/features/gigacode">GigaCode</a> — российский ИИ-ассистент для разработчиков, который работает прямо в IDE. Поддерживает GigaIDE Desktop, JetBrains IDE и другие инструменты. Работает на собственной серверной инфраструктуре без зависимости от сторонних решений.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/9ab85d52-ee3b-4d91-88c6-60ba74d78fe7.png" alt="" /></figure><p>Основное отличие от классических code completion инструментов — агентный режим. Ассистент не просто дописывает код, а понимает контекст проекта и выполняет задачи от анализа до готового решения. Находит и открывает нужные файлы, вносит изменения, запускает тесты, собирает проекты и оформляет коммиты в Git.​</p><h3>Режимы работы</h3><p><b>Line completion </b>— однострочные подсказки для быстрого дописывания кода. Работает на повторяющихся и шаблонных конструкциях: импорты, стандартные паттерны, типовые операции.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/c62ec6ec-1975-44a8-9673-6336000e2047.png" alt="" /></figure><p><b>Full-function completion</b> — многострочные подсказки, которые завершают функцию целиком или её часть. Убирает эффект «чистого листа», когда сидишь и думаешь, как начать. Пишешь сигнатуру функции, ассистент предлагает реализацию на основе контекста проекта.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/41073c8e-dae6-48d3-8770-97db4b0ca3cf.png" alt="" /></figure><p><b>Агентный режим</b> — полноценный помощник, который выполняет задачи автономно. Даёте описание задачи — он анализирует проект, находит нужные файлы, вносит изменения, проверяет через тесты, коммитит результат.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/5a98d9f1-8883-4102-8834-6a5bca912591.png" alt="" /></figure><h3>Поддерживаемые языки</h3><p>Работает с 35+ языками программирования: ABAP, C, C++, C#, Clojure, CoffeeScript, CSS, CUDA, Dart, Dockerfile, Erlang, Go, Groovy, HTML, Java, JavaScript, Julia, JSON, Kotlin, Lua, PHP, Pascal, Python, Perl, Ruby, Rust, Scala, Shell, SQL, Swift, TypeScript, TSX, VB, Vue, YAML и другие.​</p><p>Это покрывает практически весь стек современной разработки — от фронтенда на TypeScript и Vue до бэкенда на Go или Java, конфигурационные файлы вроде Dockerfile и YAML, скрипты на Shell и Python.​</p><h3>Сценарии использования</h3><ul><li><b>Для джунов и мидлов:</b> снижает порог входа в новые области. Попал в незнакомый проект на Kotlin — ассистент подсказывает идиоматичные конструкции, стандартные подходы, помогает разобраться в структуре кодовой базы.​</li><li><b>Для джунов и мидлов: </b>снижает порог входа в новые области. Попал в незнакомый проект на Kotlin — ассистент подсказывает идиоматичные конструкции, стандартные подходы, помогает разобраться в структуре кодовой базы.</li><li><b>Для команд:</b> ускорение общего процесса разработки. Когда у тебя 20 микросервисов и нужно обновить версию библиотеки во всех, агентный режим проходит по репозиториям, вносит изменения, проверяет через тесты.</li></ul><h3>Интеграция с рабочим процессом</h3><p>GigaCode встраивается в привычные IDE через плагины. Работает с GigaIDE Desktop (собственная IDE от GitVerse) и JetBrains IDE (IntelliJ IDEA, PyCharm, WebStorm, GoLand и другие). Поддержка других инструментов разработки тоже есть.​</p><p>Агентный режим взаимодействует с Git: оформляет коммиты с осмысленными сообщениями и создаёт ветки. Запускает тесты и собирает проекты через CI/CD.</p><h2>4. Kodify 2 — ещё один ИИ-инструмент для генерации кода</h2><p><a href="http://mts.ai/ru/product/kodify">Kodify</a> — российский ИИ-инструмент для генерации и автодополнения кода от МТС AI. Существует в двух версиях: Kodify 2 — полноценный on-premise сервис для корпоративной разработки, и Kodify Nano — опенсорсная модель на 1,5 миллиарда параметров, которую можно развернуть локально.​</p><p>Kodify 2 работает как сервис, готовый к использованию сразу после установки. Интегрируется с популярными IDE, разворачивается в контуре компании. Kodify Nano — облегчённая версия для разработчиков, которым не нужна корпоративная инфраструктура: генерирует код, unit-тесты и документацию, не требует мощного железа.​</p><h3>Основные возможности</h3><ul><li><b>Генерация и автодополнение кода:</b> модель создаёт код по текстовому описанию, анализирует текущие задачи, код до и после выбранного фрагмента, предлагает релевантное решение. Функции автодополнения, подсказки параметров и автоформатирования ускоряют процесс разработки.​</li><li><b>Анализ и оптимизация:</b> рефакторинг, автоматические тесты, поиск ошибок и документирование помогают быстрее находить и устранять дефекты. Можно запустить проверку кодовой базы, получить список проблемных участков с предложениями по исправлению.​</li><li><b>Многоязычная поддержка:</b> 90 языков программирования, включая Python, Java, JavaScript, C#, Go и другие. Может переводить код с одного языка на другой — полезно при миграции проектов или портировании библиотек.​</li></ul><h3>Сценарии использования</h3><p><b>Разработка продуктов: </b>автоматизация разработки, рефакторинг, оптимизация кода и поиск ошибок, перевод на другие языки программирования. Если у вас легаси на Java и нужно портировать модуль на Go, Kodify переведёт код с сохранением логики.​</p><p><b>Разработка игр:</b> генерация кода для игровых движков, создание игровой логики. Подходит для скриптинга в Unity или Unreal, когда нужно быстро набросать механики или AI-поведение NPC.​</p><p><b>Веб-разработка: </b>генерация кода для фронтенда и бэкенда сайтов и приложений. Пишешь API-эндпоинт на Python — Kodify предложит реализацию с валидацией, обработкой ошибок и документацией.​</p><p><b>Кибербезопасность:</b> анализ уязвимостей, тестирование защиты данных. Можно прогнать код через модель, получить список потенциальных проблем с безопасностью — SQL-инъекции, XSS, небезопасные зависимости.​</p><p><b>Системное администрирование:</b> автоматизация задач DevOps, настройка серверов с помощью генерируемого кода. Пишешь описание инфраструктуры — Kodify генерирует Ansible playbook или Terraform конфиг.​</p><h3>Kodify Nano — опенсорсная версия</h3><p>Kodify Nano — модель на 1,5 миллиарда параметров, доступная для локальной установки. Не предъявляет высоких требований к железу, работает на обычном ноутбуке. Генерирует код, unit-тесты и документацию.​</p><p>Доступна как плагин для JetBrains IDEs — IntelliJ IDEA, PyCharm, WebStorm, GoLand. Есть версии для macOS, Windows и Linux. Инсталляция и настройка описаны в инструкции пользователя.​</p><p>Разница между Nano и полной версией: Nano работает офлайн, не требует серверной инфраструктуры, но уступает в точности и контекстном понимании больших проектов. Kodify 2 разворачивается на серверах компании, обрабатывает более сложные сценарии, поддерживает корпоративные интеграции.​</p><h3>Интеграция и развёртывание</h3><p>Kodify 2 готов к использованию сразу после установки. Интегрируется с популярными средами разработки, разворачивается в контуре компании. Это снимает вопрос утечки кода — всё остаётся внутри периметра.​</p><p>Для on-premise установки доступна полная документация: руководство администратора, руководство пользователя, описание жизненного цикла, инструкция по установке и эксплуатации, описание функциональных характеристик.​</p><h3>Демо и тестирование</h3><p>На сайте mts.ai доступна демо-версия: можно сгенерировать код на Python или Java, протестировать функцию подсказок и автодополнения. Остальной функционал тестируется по заявке — заполняешь форму обратной связи, получаешь доступ к полной версии.​</p><h2>5. APIHOST — агрегатор ИИ-сервисов для повседневных задач</h2><p><a href="http://apihost.ru">APIHOST</a> — российская платформа, которая собирает несколько ИИ-сервисов в одном месте. Работает как веб-приложение, не требует установки. Объединяет синтез речи, транскрибацию, генерацию изображений, клонирование голоса, текстовые модели и утилиты для работы с медиа.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-01-19/b16e806b-c7d3-4c77-b6a5-f049d118b336.png" alt="" /></figure><p>Всё работает через веб-интерфейс, есть Telegram-бот для генерации изображений.​</p><h3>Основные сервисы</h3><ul><li><b>Озвучка текста:</b> уникальный синтез речи с выбором голосов, языка озвучки, настройкой высоты и эмоциональной окраски. Поддерживает русский, английский и другие языки. Можно загрузить текст напрямую, вставить ссылку на YouTube или записать голосом. Стоимость считается по символам — в интерфейсе видишь, сколько стоит озвучка до отправки запроса.​</li><li><b>Транскрибация (звук в текст):</b> преобразует аудио и видео в текст. Работает с файлами, ссылками на YouTube, записью с микрофона. Подходит для расшифровки интервью, лекций, подкастов.​</li><li><b>Клонирование голоса:</b> создаёт уникальный голос на основе загруженных образцов. Два режима — PRO-CLONE и FAST. PRO требует несколько аудиофайлов для качественной генерации, FAST работает быстрее, но менее точно. Перед генерацией система оценивает качество загруженных файлов — показывает оценку по окраске и прерывистости. Если средняя оценка удовлетворительная, можно начинать генерацию, но рекомендуется следовать комментариям возле каждого файла.​</li><li>Запись голоса с микрофона прямо в браузере — максимальная длина записи 11 секунд. Это удобно для быстрого создания образца без отдельных аудиофайлов.​</li><li><b>Нейросеть, пишущая тексты:</b> текстовая модель V1 с чатом. Вводишь запрос до 4000 символов, получаешь ответ в том же окне. Стоимость запроса — 2 рубля. Есть история запросов с возможностью восстановления старых диалогов и скачивания всей истории.​</li><li><b>Генератор изображений:</b> создаёт изображения по текстовому описанию. Одна генерация включает 4 фотографии, стоимость — 9 рублей. Описание можно писать на русском. Есть Telegram-бот @MidJourney3g для генерации через мессенджер.​</li></ul><h3>Утилиты и дополнительные функции</h3><p><b>YouTube в Mp3:</b> конвертирует аудио из ссылок на YouTube, TikTok, Facebook, VK, Rutube. Вставляешь ссылку, получаешь аудиофайл. Работает быстро, файлы доступны для скачивания в формате WAV или MP3.​</p><p><b>Изменение голоса и редактор звука:</b> обработка аудиофайлов, настройка высоты, сохранение в новом формате. Можно загрузить файл или записать через диктофон.​</p><p><b>Онлайн радио: </b>встроенный плеер с радиостанциями России. Работает как фоновая функция, если нужен доступ к радио без переключения на другие сервисы.​</p><p><b>Анализ текста: </b>подсчёт символов, слов и других метрик. Полезно для контент-мейкеров, которым нужно соблюдать ограничения по объёму.​</p><p><b>Определение IP-адреса:</b> показывает ваш текущий IP. Простая утилита, доступная в пару кликов.​</p><h3>Как работает оплата</h3><p>Система работает по предоплате. Пополняешь баланс, тратишь на запросы — каждый сервис показывает стоимость до выполнения. Озвучка считается по символам (0,02 рубля за 41 символ в примере), генерация изображений — фиксированная цена за пакет из 4 фото (9 рублей), текстовые запросы — 2 рубля.​</p><h3>API для разработчиков</h3><p>Платформа предоставляет API для интеграции сервисов в собственные проекты. Документация доступна на сайте. Это позволяет встраивать озвучку, транскрибацию, генерацию изображений в свои приложения без развёртывания отдельной инфраструктуры.</p><p>Пять инструментов — пять разных подходов к одной задаче: сделать работу быстрее и избавить от рутины.​​​​​</p><p><b>Сократик</b> помогает с созданием презентаций, когда нужно упаковать сложную информацию в слайды за минуты до созвона с фаунедром. <b>Войси</b> фиксирует всё, что обсуждалось на созвонах, и превращает часовую встречу в структурированный текст. <b>GigaCode и Kodify 2</b> берут на себя написание кода: первый через агентный режим выполняет задачи от анализа до коммита, второй работает как мощный ассистент с поддержкой 90 языков. <b>APIHOST </b>собирает несколько ИИ-сервисов в одном месте — озвучка, транскрибация, генерация изображений доступны через веб-интерфейс без настройки инфраструктуры.​​​​​</p><p>Все решения работают на российских серверах, поддерживают оплату в рублях и не требуют VPN. Большинство предлагают демо-версии или бесплатные тарифы — можете протестировать на реальных задачах до покупки.</p>]]></content:encoded>
    </item>
    <item>
      <title>Реальная Unit-экономика внедрения AI: где мы сэкономили, а где сожгли бюджет на API</title>
      <link>https://tproger.ru/articles/realnaya-unit-ekonomika-vnedreniya-ai--gde-my-sekonomili--a-gde-sozhgli-byudzhet-na-api</link>
      <comments>https://tproger.ru/articles/realnaya-unit-ekonomika-vnedreniya-ai--gde-my-sekonomili--a-gde-sozhgli-byudzhet-na-api?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Петр Сухоруких]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/realnaya-unit-ekonomika-vnedreniya-ai--gde-my-sekonomili--a-gde-sozhgli-byudzhet-na-api</guid>
      <description><![CDATA[<p>Реальный опыт внедрения AI в SERM: как GPT-4 помог снизить себестоимость обработки отзывов на 70%, почему наивная архитектура RAG сожгла бюджет, и где проходит точка безубыточности между ручным трудом и генерацией через API.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/realnaya-unit-ekonomika-vnedreniya-ai--gde-my-sekonomili--a-gde-sozhgli-byudzhet-na-api">Реальная Unit-экономика внедрения AI: где мы сэкономили, а где сожгли бюджет на API</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 09 Jan 2026 12:30:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>На пике хайпа вокруг ChatGPT казалось, что оптимизация ФОТ (фонда оплаты труда) – дело одного спринта. Задача выглядела элементарно: уволить джунов-копирайтеров, нанять одного промпт-инженера и подключить OpenAI API.</p><p>Спойлер: мы никого не уволили.</p><p>Вместо слепой веры в «волшебную таблетку» мы посчитали реальную стоимость генерации ответов, сравнили её с зарплатами и нашли точку безубыточности. Ниже – цифры, наши архитектурные ошибки и итоговая экономика проекта.</p><h2>Проблема масштабирования</h2><p>Специфика SERM (управления репутацией) –  это потоковая обработка текста. Представьте клиента с сетью на 50 филиалов, которому ежедневно падает от 100 до 300 отзывов. На каждый нужно ответить уникально, потому что антиспам-фильтры поисковиков банят копипаст.</p><p>Раньше мы решали эту задачу линейным расширением штата. Больше отзывов – больше копирайтеров. Но это тупиковый путь: люди на рутине выгорают, Tone of Voice «гуляет», а ФОТ растет быстрее прибыли. Автоматизация через LLM казалась единственным логичным выходом.</p><h2>Фаза 1. Как мы сжигали бюджет (Naive RAG)</h2><p>Первая итерация была наивной: мы подключили GPT-4, написали системный промпт с инструкциями («Ты вежливый саппорт...») и начали скармливать модели отзывы.</p><p>Экономика рухнула почти сразу. Проблема оказалась в оверхеде на токенах. Для адекватного ответа модели нужен контекст: адреса филиалов, телефоны, условия возврата, скрипты вежливости. Мы передавали этот статический контекст в System Prompt при каждом запросе.</p><p>Математика получилась убийственной:</p><ul><li>входящий отзыв – всего 50 токенов</li><li>сгенерированный ответ – около 100 токенов</li><li>но инструкция с правилами компании – 1500 токенов</li></ul><p>Мы платили за 1650 токенов, чтобы получить 100 полезных. КПД транзакции составлял ничтожные 6%. На объемах в десятки тысяч запросов ежемесячный счет за API опасно приблизился к зарплате квалифицированного разработчика.</p><figure><img src="https://media.tproger.ru/user-uploads/134965/2025-12-23/79536e37-9aec-4096-b425-97f67bea0185.png" alt="" /></figure><p><i>Пример неоптимизированного промпта. Передача статического контекста в каждом запросе убивает экономику.</i></p><p>Вторая проблема – галлюцинации. Без жестких ограничений модель начинала фантазировать. На жалобу об отсутствии парковки AI радостно отвечал: «Приносим извинения! Мы уже строим подземный паркинг, открытие в следующем месяце». Для бизнеса это прямой риск судебного иска за введение потребителя в заблуждение.</p><h2>Фаза 2. Смена архитектуры</h2><p>Решение «в лоб» оказалось экономически несостоятельным. Нам пришлось перестраивать процесс и внедрять <b>RAG (Retrieval-Augmented Generation)</b>.</p><p>Логика изменилась. Вместо того чтобы скармливать нейросети весь устав компании, мы сначала ищем в векторной базе знаний фрагмент, релевантный конкретному отзыву. Если клиент пишет про доставку, в промпт попадает только блок правил о доставке.</p><figure><img src="https://media.tproger.ru/user-uploads/134965/2025-12-23/297552cb-be29-4128-9c0b-f94522d06668.png" alt="" /></figure><p><i>Архитектура RAG. Поиск релевантного контекста через Vector DB перед генерацией снижает объем входных токенов.</i></p><p>Переход занял две недели работы бэкенда и потребовал развертывания Vector DB (мы тестировали Pinecone и Milvus). Результат того стоил: System Prompt «похудел» с 1500 до 300 токенов, а стоимость одной транзакции упала в 4 раза.</p><h2>Unit-экономика: человек vs API</h2><p>Давайте сравним себестоимость одного ответа в рублях (цены усредненные, включая налоги и инфраструктуру).</p><p><b>1. Ручной труд (Junior-копирайтер)</b>. Если сложить зарплату, налоги, стоимость рабочего места и лицензий, один сотрудник обходится компании примерно в 80 000 руб/мес. При средней выработке 70 ответов в день, Cost per Action (CPA) одного ответа составляет ~50 рублей.</p><p><b>2. Гибридная схема (GPT-4o + Оператор)</b>. Стоимость токенов (Input + Output) с учетом RAG и затрат на серверы составляет около 3-4 рублей. Но выпускать ответы без проверки нельзя – риск галлюцинаций слишком высок. Поэтому мы ввели роль оператора-валидатора. Один человек с AI-помощником проверяет до 500 ответов в день. Стоимость его работы в пересчете на единицу контента – около 8 рублей. Итоговый CPA: ~12 рублей.</p><figure><img src="https://media.tproger.ru/user-uploads/134965/2025-12-23/219a9d23-64a6-422b-9721-e114617e9d2c.png" alt="" /></figure><p>Сравнение стоимости обработки одного отзыва (RUB). Оптимизированный RAG с валидацией дешевле ручного труда на 70+%.</p><h2>Ограничения технологии</h2><p>Даже с такой экономикой есть сценарии, где мы сознательно запрещаем использование AI.</p><p>Во-первых, это сарказм и скрытая агрессия. LLM до сих пор плохо считывают контекст вроде: «Доставка – просто ракета, ждал всего три недели». Модель реагирует на слова-триггеры («ракета», «спасибо») и выдает: «Мы рады, что вы оценили скорость!». Для репутации бренда это катастрофа. Sentiment analysis помогает, но не дает 100% гарантии, поэтому такие отзывы фильтруются и уходят на ручную обработку.</p><p>Во-вторых, юридические риски. Любые досудебные претензии, угрозы жалобами в госорганы или требования компенсаций – это зона ответственности юристов. Риск автоматического ответа здесь превышает любую потенциальную экономию.</p><h2>Итог</h2><p>Внедрение AI в процессы агентства оказалось задачей не творческой, а сугубо математической.</p><p>Мы трансформировали роли: перестали нанимать джунов для написания текстов, а текущих сотрудников переквалифицировали в операторов-валидаторов. Их выработка выросла кратно, как и зарплата.</p><p>Главный вывод: без правильной архитектуры (RAG) и токеномики использование мощных моделей убыточно. Себестоимость производства контента у нас снизилась на 70%, но расходы на API и облачную инфраструктуру стали второй по величине статьей бюджета после ФОТ. И это тот компромисс, который выгоден бизнесу.</p>]]></content:encoded>
    </item>
    <item>
      <title>Платформы администрирования и мониторинга баз данных на основе PostgreSQL</title>
      <link>https://tproger.ru/articles/platformy-administrirovaniya-i-monitoringa-baz-dannyh-na-osnove-postgresql-259927</link>
      <comments>https://tproger.ru/articles/platformy-administrirovaniya-i-monitoringa-baz-dannyh-na-osnove-postgresql-259927?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Валерия Турчак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/platformy-administrirovaniya-i-monitoringa-baz-dannyh-na-osnove-postgresql-259927</guid>
      <description><![CDATA[<p>Как выбрать инструмент для администрирования PostgreSQL — разбираем коммерческие решения от российских вендоров.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/platformy-administrirovaniya-i-monitoringa-baz-dannyh-na-osnove-postgresql-259927">Платформы администрирования и мониторинга баз данных на основе PostgreSQL</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 26 Dec 2025 12:20:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Управление базами данных — это не только написание SQL-запросов. Здесь нужно разворачивать кластеры, настраивать мониторинг, понимать, когда надо срочно обновлять СУБД, если найдены уязвимости, организовывать резервное копирование, распределять права доступа между командой — и при этом не тратить недели на изучение документации.</p><p>На российском рынке доступно несколько инструментов, которые решают эти и многие другие задачи, связанные с администрированием PostgreSQL-инфраструктуры. Каждый — со своими функциональными и архитектурными особенностями.</p><h2>1. Платформа Tantor</h2><p><a href="https://tantorlabs.ru/products/platform?utm_source=tproger&amp;utm_medium=native">Платформа Tantor</a> позволяет осуществлять управление,  администрирование и мониторинг множества корпоративных баз данных на PostgreSQL и любых его форках (Tantor Postgres, PostgresPro, Pangolin и других). Объединяет в одном интерфейсе инструменты для конфигурирования, обслуживания, профилирования запросов и работы с кластерами Patroni. Все функции доступны из браузера без установки дополнительного ПО на рабочее место администратора. Продвинутая система алертов (оповещений) в платформе Tantor автоматически отслеживает метрики и уведомляет администраторов о потенциальных проблемах, позволяя максимально быстро на них реагировать.</p><h3>Для кого это работает</h3><ul><li><b>DBA</b> используют платформу как центр управления всеми PostgreSQL-инстансами: мониторят нагрузку, блокировки и репликацию, настраивают параметры через группы конфигураций, планируют регламентные работы.</li><li><b>SRE и дежурные инженеры</b> следят за дашбордами по доступности кластеров, работают с алертами на превышение порогов по времени ответа или лагу репликации. При инцидентах используют алерты и алгоритмический ассистент для расшифровки ошибок и базовой диагностики.​</li><li><b>Разработчики</b> заходят эпизодически — посмотреть профиль нагрузки своих сервисов, разобрать планы выполнения проблемных запросов или получить  SQL-выборку по описанию на естественном языке через алгоритмический ассистент.​</li><li><b>Команды безопасности</b> настраивают маскирование конфиденциальных данных для тестовых контуров и проверяют аудит действий пользователей.</li></ul><h2>Алерты (оповещения)</h2><ul><li><b>Непрерывный мониторинг:</b> агенты платформы постоянно собирают метрики (например, загрузку процессора, состояние процессов БД) и анализируют эти данные на предмет аномалий. Можно редактировать условия срабатывания. Например, система может автоматически создать алерт, если загрузка процессора сервера БД долгое время превышает 80%, а при сбое в отказоустойчивом Patroni-кластере алерт мгновенно уведомит администратора о необходимости вмешательства. Также если запланированная задача (например, резервное копирование) не выполнилась или завершилась с ошибкой, в интерфейсе появится соответствующее уведомление.</li><li><b>Классификация по критичности:	</b>алерты имеют цветовую индикацию: красный — высокая критичность, желтый — предупреждение. Все алерты отображаются в едином интерфейсе (меню «колокольчик»), где их можно фильтровать, сортировать и просматривать детали.</li><li><b>Внешние уведомления:</b> интеграции позволяют направлять алерты в Telegram, электронную почту, Mattermost. Алерты разного уровня критичности можно направлять в разные каналы.</li><li><b>Анализ и история: </b>для каждого алерта доступны детали (метрики, хост), связанные события и полный журнал изменений (Events Log).</li></ul><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/85f40b8c-6c71-46d5-a6ab-b40c960d849b.png" alt="" /></figure><h3>Управление Patroni-кластерами</h3><p>Платформа визуализирует топологию кластера: кто лидер, кто реплика, какой lag, статус health. Плановый switchover выполняется из интерфейса без ручных команд. Если одна из реплик отстаёт или упала, система фиксирует это на дашборде и отправляет алерт.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/3cc2b2ff-2a45-48c0-9f11-13d9e3f11dd3.png" alt="" /></figure><h3>Профилирование и оптимизация запросов</h3><p>Query Profiler на базе pg_stat_statements собирает статистику по запросам на выбранном промежутке времени. Группирует их, показывает время выполнения, количество вызовов, детализацию по каждому.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/ddc4b638-72cd-481d-bbdf-4f3247f1ab43.png" alt="" /></figure><p>Для «тяжелых» запросов формирует визуализацию планов и рекомендации по оптимизации — добавить индекс, переписать join, изменить параметры планировщика.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/82a2d54b-0b3e-4e9c-8998-104d25600cae.png" alt="" /></figure><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/24a5901b-ee50-473a-9b1e-68744897354c.png" alt="" /></figure><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/aec374a5-a91e-48ae-b676-0ef71c05bc95.png" alt="" /></figure><h3>Конфигурирование СУБД под разные профили нагрузки</h3><p>Платформа Tantor позволяет из одной точки управлять конфигурациями кластеров и инстансов. Кроме этого, у пользователя есть возможность создать типовые профили нагрузки, например 1С или BI-платформу, автоматизированную банковскую систему или иные ресурсоемкие бизнес-приложения. Это позволяет более тонко управлять имеющимися ресурсами хранения данных и их вычисления за счет возможности сконфигурировать сразу большую группу инстансов одной конфигурацией с помощью групповых параметров.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/97769833-6578-48d0-8227-ab30b4208d5d.jpg" alt="" /></figure><h3>Анонимизация данных</h3><p>Модуль на основе pg_anon ищет конфиденциальную информацию в таблицах и создаёт анонимизированные дампы. Настраиваются правила маскирования для конкретных полей, после чего формируется выгрузка или копия базы для передачи разработчикам. Данные остаются репрезентативными по объёму и структуре, но без реальных значений.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/3f1161b9-6fc6-45ee-bd71-1c0e012272ee.png" alt="" /></figure><h3>Планировщик и автоматизация</h3><p>Создание задач для выполнения SQL-скриптов или команд ОС по расписанию или с указанием периодичности. Подходит для регламентных работ — vacuum, analyze, проверка bloat, архивирование логов. История выполнения и результаты сохраняются в интерфейсе.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/1c615cf7-6207-4d49-8e40-41af87e70889.png" alt="" /></figure><h3>Лог-аналитика</h3><p>Платформа собирает и хранит логи СУБД с возможностью поиска по тексту, пользователям, объектам БД и кодам ошибок. При разборе инцидентов это позволяет связать проблемы на уровне приложения с реальными событиями в базе без grep по десяткам файлов.</p><h3>Алгоритмический ассистент</h3><p>Взаимодействует с метаданными БД, логами, оптимизатором запросов и метриками. Выполняет семантический поиск, классифицирует данные, генерирует SQL по текстовому описанию, предлагает оптимизации и помогает интерпретировать планы выполнения.</p><h3>Технические детали</h3><ul><li>Веб-интерфейс, доступный из браузера на любой ОС.</li><li>Развёртывание on-premise через docker-compose на Linux (включая Astra Linux).</li><li>Поддержка всех версий PostgreSQL и коммерческих форков.</li><li>REST API для экспорта метрик и выборок данных.</li><li>Интеграция с SIEM через syslog, с мессенджерами (Telegram, Mattermost) и почтой.</li><li>Ролевая модель доступа, рабочие пространства, журнал событий.​</li></ul><h3>Лицензирование</h3><p>Коммерческая лицензия по количеству серверных ядер обслуживаемых (наблюдаемых ) СУБД. Бесплатная полнофункциональная версия работает до 180 дней. Включена в Единый реестр российского ПО (запись 12905).</p><h3>Поддержка</h3><p>Документация полностью на русском языке, есть обучающие материалы на <a href="https://youtube.com/@tantorlabs/playlists">YouTube</a>, <a href="https://rutube.ru/channel/31876555/playlists/">Rutube</a> и <a href="https://vk.com/tantorlabs">VK</a>. Компания проводит вендорские курсы и сертификацию через авторизованные учебные центры. Для корпоративных клиентов доступна техподдержка 24/7 с SLA от 2 часов в зависимости от критичности запроса.</p><h2>2. Platform V Kintsugi</h2><p><a href="https://platformv.sbertech.ru/products/rabota-s-dannymi/kintsugi">Инструмент</a> для мониторинга и сопровождения СУБД, основанных на PostgreSQL, который позволяет обслуживать тысячи баз данных и выполнять задачи администрирования в едином интерфейсе. Продукт зарегистрирован в Едином реестре российских программ и позиционируется как альтернатива Oracle Enterprise Manager Cloud Control.</p><h3>Для кого это работает</h3><p>Platform V Kintsugi также подходит для компаний, которые ищут замену инструментам мониторинга иностранных вендоров. Решение масштабируется от среднего бизнеса с небольшими объёмами баз данных до крупных компаний с высоконагруженными системами — продукт выдерживает промышленные нагрузки Сбера.</p><h3>Централизованное наблюдение</h3><p>Оперативный центр даёт возможность получать информацию о состоянии множества СУБД в одном окне. Предустановлены панели мониторинга с основными метриками, есть опция добавления собственных метрик мониторинга.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/84e2e366-818e-4075-bbfa-af5e9f9b983f.png" alt="" /></figure><p>В оперативном центре можно отслеживать события БД практически в режиме реального времени, чтобы контролировать инциденты до их эскалации.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/9343e428-1fc8-40d4-95de-ec09ee7a7593.png" alt="" /></figure><h3>Работа с экземплярами БД</h3><p>Обзорная панель СУБД проводит постоянный мониторинг экземпляров и отправляет важную информацию о статусе баз данных в одном месте. Функциональность решения ускоряет обслуживание инфраструктуры и автоматизирует повторяющиеся задачи по сопровождению СУБД.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/e58dbfcb-ff6e-4b12-b091-8de529c97683.png" alt="" /></figure><h3>SQL-интерфейс с аудитом</h3><p>Безопасный терминал для выполнения запросов оснащен функцией аудита действий пользователя, что повышает уровень безопасности при работе с данными. Решение соответствует стандартам защищённости в каждой функциональной части и гарантируют безопасность при работе с критической инфраструктурой.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/df2ad528-54bc-4849-983d-3c9f21dfa67c.png" alt="" /></figure><h3>Анализ производительности</h3><p>История активности сессий помогает оптимизировать производительность любой PostgreSQL-подобной СУБД без необходимости использования дополнительного программного обеспечения. В интерфейс продукта интегрированы отчёты pg_profile. Решение помогает анализировать и проводить диагностики без влияния на производительность СУБД.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/9b3f1f1d-fd23-4904-964f-4edc08b0b379.png" alt="" /></figure><h3>Визуализация и оптимизация запросов</h3><p>Понятная визуализация планов для подробного анализа пользовательских запросов, чтобы улучшить понимание и оптимизацию выполнения. Инструмент собирает и обрабатывает метрики баз данных и находит узкие места в работе системы.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/afc70e0e-10a7-422f-b80f-bf9f76830eb1.png" alt="" /></figure><h3>Лицензирование</h3><p>Продукт зарегистрирован в Едином реестре российских программ для ЭВМ и баз данных. Реестровая запись: №18302 от 12.07.2023.</p><h3>Особенности архитектуры</h3><p>Инструмент оснащен изменяемой информационной моделью и широким интеграционным слоем. Совместим с 80+ решениями Platform V. Встроена поддержка отечественной СУБД Platform V Pangolin DB для высоконагруженных систем, при этом поддерживается работа с любыми PostgreSQL-like СУБД. Документацию можно изучить <a href="https://platformv.sbertech.ru/docs/public/DBM/1.8.0/index.html">здесь</a>.</p><h2>3. Postgres Pro Enterprise Manager</h2><p><a href="https://postgrespro.ru/products/PPEM">PPEM</a> — графическая платформа для управления базами данных Postgres Pro через веб-интерфейс. Инструмент обеспечивает мониторинг, диагностику и управление множеством экземпляров Postgres Pro в рамках собственной инфраструктуры.​</p><h3>Снижение порога входа</h3><p>Понятная структура интерфейса, визуализация данных и подробная информация о статусе операций снижают порог вхождения в администрирование СУБД. Для выполнения рутинных операций не требуется знать команды SQL. Автоматизация задач, визуализация метрик и планов запросов, интеграция с pgpro_pwr и pg_probackup ускоряют работу и упрощают анализ производительности.​</p><h3>Централизованный дашборд</h3><p>На главном дашборде отображается основная информация о базах данных, подключённых к PPEM: общий размер, последние сообщения из журналов, статус выполнения задач. Обеспечивается постоянный контроль в режиме реального времени 24/7.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/f576e598-f144-4cf4-9204-eb406de1796c.png" alt="" /></figure><h3>Управление экземплярами</h3><p>Раздел «Экземпляры» показывает список всех подключённых баз данных с основной информацией: версия PostgreSQL, каталог данных, порт, сервер, роль (standalone), количество баз данных, транзакций в секунду, соединений. Доступны базовые действия: остановка, запуск, резервное копирование, настройки. PPEM работает с любой редакцией Postgres Pro.​</p><p>Инструмент поддерживает автоматическое обнаружение экземпляров в сети (autodiscovery-режим), что сокращает время подключения новых баз и упрощает масштабирование.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/d283e8aa-89aa-48cd-931f-12cae76c09d1.png" alt="" /></figure><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/fba56782-20a4-41e6-ad71-ec75e6261eb4.png" alt="" /></figure><h3>Мониторинг и диагностика</h3><p>Для каждого экземпляра доступны графики ключевых метрик производительности, информация по сеансам БД и событиям ожидания, выполняющимся SQL-запросам. Можно просматривать и редактировать параметры экземпляра. Есть возможность добавлять собственные показатели производительности и адаптировать мониторинг под особенности инфраструктуры.​</p><h3>Работа с базами данных</h3><p>Список баз данных предоставляет детальную информацию: объём, количество таблиц и индексов, bloat (коэффициент распухания), wraparound (предупреждение о зацикливании счётчика транзакций). Это помогает выявлять узкие места и реагировать до появления проблем.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/25a5c007-5afc-4e86-ae2a-3525b430601a.png" alt="" /></figure><h3>Интеграция с pgpro_pwr</h3><p>Встроенный диагностический профайлер нагрузки формирует историю статистики. Модуль pgpro_pwr автоматически создаёт снимки, строит и визуализирует отчёты. Можно конфигурировать целевые серверы, настраивать расписание снимков и срок хранения данных. Это позволяет анализировать наиболее ресурсоёмкие операции в базе данных.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/cc52bf19-b2c6-47d1-8573-b7688d8e194a.png" alt="" /></figure><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/e45828c7-0a95-4db6-b831-3973be4a58f2.png" alt="" /></figure><h3>Управление табличными пространствами</h3><p>Возможность создания, редактирования и удаления Tablespace. При создании табличного пространства настраиваются параметры: название, каталог, владелец, seq_page_cost, random_page_cost, effective_io_concurrency, maintenance_io_concurrency, compression.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/331b29c9-e63a-42d5-a55d-2a7be740e013.png" alt="" /></figure><h3>Резервное копирование и восстановление</h3><p>Интеграция с pg_probackup позволяет настраивать резервное копирование по расписанию и восстанавливать данные за несколько кликов. В интерфейсе отображается список резервных копий с информацией: ID, размер, начало и окончание выполнения, режим (FULL), хранилище, экземпляр, статус, пользователь, статус валидации, начало и окончание валидации. Можно посмотреть детальный журнал выполнения или вызвать форму восстановления из выбранной резервной копии.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/905c5ebf-93bc-43e4-af1d-8bc18b32573d.png" alt="" /></figure><h3>Планирование и автоматизация</h3><p>Настройка расписаний для обслуживания базы: вакуум, анализ, реиндексация и другие операции. Все процессы прозрачны — в консоли заданий видны статус, длительность и результат выполнения. Это ускоряет обслуживание инфраструктуры и автоматизирует рутинные повторяющиеся задачи.​</p><h3>Журнал событий</h3><p>Графический интерфейс к журналу событий экземпляра БД с возможностями выбора временнóго интервала, фильтрации и текстового поиска. Журнал показывает детали каждого события: дата, время, экземпляр, пользователь, база данных, тип, сообщение.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/caeb041b-2ef0-4b82-84b6-d0bbe0886f12.png" alt="" /></figure><h3>Управление доступом</h3><p>Система ролей и встроенная аутентификация позволяют точно настроить права доступа. Гибкое распределение ролей помогает ограничить доступ к важным данным и избежать случайных ошибок. Все действия — от задач до изменений в конфигурации — фиксируются в истории. Можно интегрировать PPEM с LDAP для централизованной аутентификации.​</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-12-24/d7ff1bf7-a4cf-4591-bff0-8ce48f40468b.png" alt="" /></figure><h3>Лицензирование</h3><p>Postgres Pro Enterprise Manager входит в состав всех редакций Postgres Pro и не имеет ограничений на количество ядер процессора или сервера и количества пользователей. Регистрация (№104 в Едином реестре российского ПО).</p><h3>Особенности развёртывания</h3><p>PPEM не требует внедрения сторонних решений — всё в одном инструменте, без дополнительных затрат и рисков. Можно развернуть в изолированной среде без доступа в интернет. Интуитивно понятная структура, ролевая модель доступа, встроенная история действий и консоль заданий обеспечивают удобную и безопасную работу с БД.</p><h2>В результате</h2><p>Если у вас уже есть коммерческая СУБД одного из российских вендоров, то вы вместе с ней в комплекте скорее всего получили и графическую платформу управления БД. Но если текущая платформа вас не устраивает, то можете выбрать из обзора.</p>]]></content:encoded>
    </item>
    <item>
      <title>Сжать государственную VIN-базу с 1,5 ГБ до 21 МБ? Реально! Разработчик рассказал как</title>
      <link>https://tproger.ru/news/szhat-gosudarstvennuyu-vin-bazu-s-1-5-gb-do-21-mb--realno--razrabotchik-rasskazal-kak</link>
      <comments>https://tproger.ru/news/szhat-gosudarstvennuyu-vin-bazu-s-1-5-gb-do-21-mb--realno--razrabotchik-rasskazal-kak?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/szhat-gosudarstvennuyu-vin-bazu-s-1-5-gb-do-21-mb--realno--razrabotchik-rasskazal-kak</guid>
      <description><![CDATA[<p>Разработчик показал, как сократить государственную VIN-базу с 1,5 ГБ до 21 МБ: анализ данных, удаление лишних таблиц, индексов и грамотная оптимизация под чтение</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/szhat-gosudarstvennuyu-vin-bazu-s-1-5-gb-do-21-mb--realno--razrabotchik-rasskazal-kak">Сжать государственную VIN-базу с 1,5 ГБ до 21 МБ? Реально! Разработчик рассказал как</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 25 Dec 2025 09:27:38 GMT</pubDate>
      <content:encoded><![CDATA[<p>Команда разработчиков офлайн-декодера VIN — <b>Corgi</b> — столкнулась с типичной проблемой государственных данных.</p><p>База VPIC от National Highway Traffic Safety Administration <b>весила 1,5 ГБ</b>. Для серверов это нормально, но для браузеров, edge-платформ и мобильных устройств — <b>почти приговор</b>.</p><p>У такой базы целый ворох проблем. От долгого скачивания и съедания чрезмерной памяти, до поломки serverless-запусков и невозможности работать с приложениями, которые должны запускаться «везде».</p><p>При этом именно VPIC считается официальным и самым полным источником данных для расшифровки VIN в США.</p><h2>Анализ вместо «магической оптимизации»</h2><p>Первое, что сделали разработчики — это <b>разобрали базу по частям</b>. С помощью SQLite-инструментов они посмотрели, какие таблицы реально занимают место.</p><p>Оказалось, что <b>почти 850 МБ</b> приходилось на одну таблицу, которая хранила соответствие кодов производителей и модельных годов.</p><p>Ключевой вывод: <b>эти данные можно вычислять на лету из других таблиц</b>. Таблицу удалили и база сразу похудела больше чем наполовину.</p><p>Дальше команда пошла по тому же пути: <b>не сжимать, а выкидывать лишнее</b>. Из базы убрали шаблоны и справочники, которые нужны регуляторам, но почти никогда не используются в прикладных VIN-декодерах.</p><h2>Оптимизация под реальное использование</h2><p>Следующий шаг — чистка «мертвых» данных. После удаления части таблиц, <b>в базе остались записи, на которые больше никто не ссылался</b>. Их тоже убрали.</p><p>Затем команда пересобрала индексы. Оригинальная база оптимизирована под регулярные обновления — ведь государство ее постоянно дополняет. Corgi же использует базу только для чтения.</p><p>Это позволило удалить лишние индексы и создать новые, заточенные под самые частые запросы при декодировании VIN.</p><p>И финальный штрих — VACUUM, чтобы SQLite физически пересобрал файл без мусора.</p><h2>Результат: минус гигабайты, плюс скорость</h2><p>В итоге <b>размер базы сократился с 1,5 ГБ до 64 МБ</b> в несжатом виде. <b>После gzip — всего 21 МБ</b>. При этом декодирование стало даже быстрее: таблицы меньше, индексы точнее, а лишних данных нет.</p><p>Оптимизированную базу упаковали в open-source библиотеку Corgi, которая работает офлайн и одинаково подходит для сервера, браузера и edge-окружений.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как мигрировать на Node.js 22 без рисков: инструкция</title>
      <link>https://tproger.ru/articles/kak-migrirovat-na-node-js-22-bez-riskov--instrukciya</link>
      <comments>https://tproger.ru/articles/kak-migrirovat-na-node-js-22-bez-riskov--instrukciya?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Светлана Гринь]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-migrirovat-na-node-js-22-bez-riskov--instrukciya</guid>
      <description><![CDATA[<p>Node.js 22 стал надёжным стандартом для компаний, которым важно сохранить стабильность после завершения поддержки прежних версий. Разберём практические шаги по безопасной миграции. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-migrirovat-na-node-js-22-bez-riskov--instrukciya">Как мигрировать на Node.js 22 без рисков: инструкция</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 14 Dec 2025 12:40:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Node.js 22 сегодня становится дефолтным выбором для компаний, которые откладывали обновление годами. Вокруг этой версии сформировалась зрелая экосистема, а крупные команды уже успели протестировать релиз в продакшене. Разберём, что думают разработчики о платформе и какие подводные камни вас ждут при миграции.</p><h2>Почему релиз Node.js 22 важен</h2><p>30 апреля 2025 года завершился жизненный цикл версии Node.js 18. Сообщество заранее предупреждало об этом через официальный аккаунт в X. Там напомнили разработчикам о необходимости перейти на более свежие версии — Node.js 20 или 22. Восемнадцатая версия перестала получать патчи безопасности, поэтому обновление стало критичным для поддержания стабильной работы приложений.</p><p>Наиболее надёжным вариантом стала миграция на Node.js 22, которая сейчас находится в статусе долгосрочной поддержки (LTS) и гарантирует обновления безопасности и стабильность на ближайшие годы.</p><p>Хотя релиз этой версии не выглядел революционным, он существенно изменил основу платформы. Многие возможности, которые раньше требовали внешних библиотек, теперь встроены прямо в платформу: WebSocket-клиент, стабильный fetch(), работа с файлами, Blob и URL, улучшенная совместимость ESM и CommonJS. Это уменьшает количество зависимостей, снижает вероятность уязвимостей и значительно упрощает поддержку проектов.</p><p>Помимо удобства разработки, в новой версии усилились производительность и безопасность. Обновлённый движок V8 ускоряет выполнение кода, а оптимизация старта и снижение потребления памяти делают Node.js более стабильным в высоконагруженных и serverless-сценариях. Улучшенная модель разрешений, строгие настройки TLS и расширенная поддержка AbortController повышают защиту на уровне рантайма, ограничивая последствия уязвимостей в сторонних пакетах. Это даёт компаниям возможность строить более надёжные сервисы с меньшими затратами на контроль рисков и эксплуатацию.</p><p>Node.js 22 также стал удобнее для работы гибридных команд. Стабильная интеграция ESM и CommonJS облегчает миграцию старых проектов, а унификация Web-API делает код универсальным и понятным даже для сотрудников без глубокого опыта в бэкенде. Улучшенные инструменты тестирования и диагностики повышают прозрачность разработки и снижают время на нахождение ошибок.</p><blockquote>Node.js 22 — эволюционный релиз. Он развивает ранее введённые возможности (fetch, ESM, WebSocket и др.), но не переворачивает парадигму разработки. Это укрепление уже заданного курса.</blockquote><h2>Возможности Node.js 22</h2><p><b>Расширение JavaScript и обновление V8.</b> Получив движок V8 12.x, Node.js подтянул все улучшения ECMAScript 2024. Код стал исполняться быстрее как за счёт оптимизаций в компиляторе, так и благодаря улучшенной работе с асинхронностью. Например, async/await теперь обрабатываются эффективнее, а создание структур данных, активно используемых в серверах, стало менее затратным.</p><p>С точки зрения разработчика изменения могут быть неочевидны, но приложения на реальной нагрузке выполняются быстрее, стабильнее и предсказуемее.</p><p><b>Corepack включён по умолчанию.</b> До выхода Node.js 22 разработчики сами решали, подключать ли Corepack. Теперь он активен без дополнительной настройки, и это серьёзное изменение в экосистеме. Все менеджеры пакетов контролируются единой прослойкой, что устраняет зависимость от версии, установленной глобально у разработчика или в CI.</p><p>На практике это означает, что версия менеджера пакетов становится частью проекта и перестаёт зависеть от среды. Ошибки вида «у меня работает, а у коллеги нет» заметно сокращаются. Благодаря этому процессы разработки и сборки становятся стабильнее.</p><blockquote>Corepack, как мне кажется, сильно упростит жизнь большим командам. Исчезает вечная история “а у меня Yarn другой версии” или “Pnpm не подтянулся”. Corepack берёт на себя управление версионированием менеджеров пакетов, а значит, сборки становятся более воспроизводимыми. Для корпоративных проектов это плюс, особенно если речь идёт о распределённых командах или CI/CD-конвейерах.</blockquote><p><b>WebSocket API и улучшенные Web Streams.</b> Node.js 22 внедрил нативную поддержку WebSocket API. Теперь можно создавать WebSocket-клиенты и серверы без сторонних библиотек вроде ws. Это делает код чище и ближе к браузерному API.</p><p>Параллельно были улучшены Web Streams. Работа с потоками стала предсказуемой, а инструменты вроде CompressionStream и DecompressionStream функционируют так же, как и в современных браузерах. Поддержка универсального кода — сервер + клиент — стала проще, а объём внешних зависимостей уменьшился.</p><p><b>Улучшения в работе CommonJS и ESM.</b> Node.js постепенно движется в сторону ESM, но у огромного количества проектов всё ещё используется CommonJS. В новой версии улучшена производительность смешанных проектов и сокращены ошибки при импорте модулей между двумя системами. Между ними появилось меньше «подводных камней», а значит миграция на ESM может идти постепенно, без ломки архитектуры.</p><p><b>Стабильный встроенный fetch().</b> Его реализация теперь полностью совпадает с браузерной, так что в простых кейсах можно отказаться от axios или node-fetch. Меньше зависимостей — быстрее запуск и меньше технического долга.</p><p><b>Развитие модели разрешений</b>, которая позволяет ограничивать доступ приложения к файловой системе, сети или переменным окружения прямо на уровне рантайма. Даже если зависимость уязвима, код не сможет выйти за пределы разрешённых прав. Это огромный шаг в сторону реальной безопасности.</p><blockquote>Я бы отметил более зрелую работу с Permission Model. Она уже не экспериментальная и реально помогает ограничивать доступ к файловой системе и сети. Параллельно улучшилась производительность V8, а вместе с ней и общая отзывчивость серверных приложений. Подтянули и Web-стек: стабильнее стали Web Streams, Request/Response и другие API, которые сближают Node с браузерами. Всё это не революция, но тренд на унификацию растёт.</blockquote><p><b>Расширенные возможности тестирования.</b> Node.js продолжает развивать встроенный тестовый фреймворк node:test. Теперь доступны кастомные репортеры, покрытие кода без внешних инструментов и mock timers (замена реальной работы таймеров). Это позволяет использовать встроенный тест-раннер для большинства задач, отказавшись от Jest или Vitest в простых проектах.</p><p><b>Обновления в безопасности.</b> Обновление OpenSSL — одно из самых чувствительных изменений в релизе. Поддержка старых криптоалгоритмов исключена, проверка сертификатов стала строже, а взаимодействие по TLS 1.3  стабильнее.</p><blockquote>С точки зрения безопасности рывка не произошло, но заметное движение есть: Permission Model, улучшенная работа с OpenSSL, более аккуратная валидация входящих данных. Всё это снижает риски, но полностью опасность не снимает, потому что реальная безопасность всё равно лежит в архитектуре, процессов CI/CD и культуре разработки.</blockquote><p><b>Повышенная производительность.</b> Заметные улучшения коснулись старта приложения, работы с памятью и производительности под нагрузкой. Приложения запускаются быстрее, а распределение нагрузки между worker threads стало более эффективным. Это позволит обслуживать больше запросов при тех же ресурсах и уменьшить расходы на инфраструктуру.</p><p><b>Международные стандарты и локализация.</b> Были обновлены Intl API на базе новой версии ICU. Преимущества: точные часовые пояса, поддержка новых календарей и систем чисел. Это критично для глобальных SaaS-платформ, аналитических сервисов и финтех-продуктов.</p><p>По словам Даниила Гоника, для разработчиков наиболее значимы такие изменения, как WebSocket-клиент, модульная система., обновление V8, улучшение JIT-компиляторов, добавление новых возможностей JS и улучшение встроенного test-раннера.</p><h2>Как безопасно мигрировать на Node.js 22</h2><p>Переход лучше начинать с <b>аудита и подготовки окружения</b>. Сначала стоит убедиться, что все зависимости поддерживают Node.js 22. Особенно это касается библиотек, которые используют нативные модули, криптографию и WebSockets. Проверка совместимости позволит избежать неожиданностей вроде ошибок компиляции или падений при запуске.</p><p>После этого нужно прогнать <b>тесты</b>. Даже минимальный набор позволит выявить проблемы, связанные с изменениями в ESM/CJS, WebSocket API или OpenSSL.</p><p>Само <b>обновление среды</b> зависит от выбранного инструмента. Через nvm или n процесс занимает секунды, а для Docker достаточно указать новый базовый образ. Если проект использует CI/CD, стоит уделить внимание Corepack: теперь он активен всегда, что может изменить поведение сборки.</p><p>После обновления полезно <b>проверить логи</b> и внимательно <b>изучить предупреждения</b>. Большинство проблем решается установкой последних версий библиотек, иногда — правкой конфигурации TLS или обновлением менеджера пакетов.</p><p>Обновление стоит внедрять поэтапно, начиная со staging или частичного продакшена, и держать готовность к откату.</p><blockquote>Я обычно советую подход двухконтурного обновления. Сначала поднять проект на 22-ю версию в отдельной среде, включить максимальный объём логов, запустить тесты и прогнать реальные сценарии. Потом обновить линтеры, сборщики и самые старые пакеты, чтобы убрать накопленные за годы предупреждения. И только когда всё стабильно, имеет смысл переключать продакшен. По сути, обновление Node нужно делать так же аккуратно, как обновление облачных сервисов: через изоляцию, наблюдаемость и бэкап-стратегию.</blockquote><blockquote>В Node.js 22 удалены некоторые устаревшие, малоиспользуемые функции, поэтому при переходе может потребоваться рефакторинг мест, где они использовались. Следует проверить депрекейты: Node.js 22 помечает ряд старых возможностей как устаревшие и предлагает альтернативы. Необходимо убедиться, что все библиотеки и пакеты, используемые проектом, поддерживают новую версию Node.js.<br />При скачке сразу с 18/20 на 22 некоторые зависимости могут оказаться несовместимыми с обновлённым движком V8 или новыми APIs — может понадобиться их обновление до последних версий.</blockquote><h2>Кому стоит подождать с переходом</h2><p>Переход на Node.js 22 подходит не всем проектам. В некоторых ситуациях разумнее подождать, чтобы избежать регрессий и поломок в продакшене. Вот кому стоит повременить с переходом:</p><ul><li>Тем, кто использует инструменты сборки или менеджеры пакетов, которые ещё не совместимы с Node.js 22 и могут выдавать ошибки или не работать вовсе.​</li><li>Командам, которые работают на старых версиях ОС. Известны проблемы совместимости с macOS 10.14 и ниже, а также возможны ошибки на Windows при миграции старых приложений.​</li><li>Тем, чьи CI/CD, контейнеры или Docker-образцы завязаны на конкретные версии Node.js и npm. Переход на 22-ю версию требует много регрессионного тестирования.​</li><li>Проектам с критически важными C++-добавками. Смена major-версии влечёт за собой обновление бинарных интерфейсов, что может вызывать ошибки при компиляции нативных модулей.</li><li>Пользователям определённых интеграций. Для некоторых версий MongoDB‑драйверов зафиксированы фатальные баги после перехода на конкретные промежуточные версии Node.js 22.x.​</li><li>Если вы используете проекты, которые сами рекомендуют Node.js 18 или 20, а обновление их зависимостей под 22 только планируется.</li></ul><blockquote>В продакшене чаще всего проблемы возникают не из-за самих изменений в платформе, а из-за того, как эти изменения проявляются в реальных проектах. Где-то обновился V8 и стал вести себя строже, где-то повылезали deprecated-модули, а где-то просто сломались сборщики или старые зависимости. Многие команды до сих пор живут на Webpack 4, Express 4 или TypeORM старых версий: вот они первыми столкнутся со сложностями.</blockquote><h2>Что дальше</h2><p>Среди будущих трендов в экосистеме <a href="http://node.js/">Node.js</a> Максим Захаренко выделяет следующие: <i>«Первое — постепенное сближение Node с Web-стеком, чтобы код становился максимально универсальным. Второе — рост влияния Bun и Deno, которые будут подталкивать Node к более агрессивной оптимизации. И третье — всё более активная интеграция с AI-инструментами, особенно в DevTools и в цепочках генерации кода»</i>.</p><blockquote>Всё больше веб-API внедряются в Node.js (fetch, WebSocket, EventTarget). Этот тренд будет продолжаться. ESM становится стандартом, поддержка будет расширяться, CommonJS постепенно уйдёт в прошлое. Расширяется WASI и поддержка WASM-модулей. Node.js движется в сторону мульти-языковой среды. Модель прав доступа и другие sandbox-механизмы также продолжат развиваться. Кроме того, в версии Node.js 22.5.0 экспериментально добавлен встроенный модуль SQLite (через –experimental-sqlite). Это важное уточнение: в релизе 22.0.0 SQLite отсутствовал.</blockquote><p>Даниил Гоник хотел бы увидеть в будущих релизах стабильную и удобную модель разрешений, возможность require() для ESM без флагов, улучшения в DevX: поддержка TS «из коробки», больше инструментов в ядре, расширение SEA (Single Executable Apps) и встроенные механизмы верификации зависимостей (подписи, integrity).</p><h2>Выводы</h2><p>Node.js 22 делает приложения быстрее, безопаснее и совместимее с Web API, улучшает работу с памятью и снижает зависимость от глобальных инструментов. Хотя переход требует подготовки, он не представляет серьёзных рисков, если следовать базовой процедуре: проверить зависимости, прогнать тесты и обновить среду постепенно. Если инфраструктура современная, а стек устойчивый, переход на Node.js 22 принесёт ощутимые преимущества как в скорости работы, так и в удобстве разработки.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как метрика Time-to-Optimize реально экономит деньги на облаке</title>
      <link>https://tproger.ru/articles/kak-metrika-time-to-optimize-realno-ekonomit-dengi-na-oblake</link>
      <comments>https://tproger.ru/articles/kak-metrika-time-to-optimize-realno-ekonomit-dengi-na-oblake?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дмитрий Важенин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-metrika-time-to-optimize-realno-ekonomit-dengi-na-oblake</guid>
      <description><![CDATA[<p>Рассказываем, что такое Time to Optimize, как ее измерять и почему снижение TTO в 10 раз может дать ту же экономию, что и пересмотр тарифов у провайдера.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-metrika-time-to-optimize-realno-ekonomit-dengi-na-oblake">Как метрика Time-to-Optimize реально экономит деньги на облаке</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Облачные технологии]]></category>
      <category><![CDATA[Аналитика]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 30 Nov 2025 11:40:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Time-to-Optimize – ключевая метрика для FinOps</p><p>Метрики, KPI и дашборды – это основа деятельности любой компании, претендующей на отлаженность процессов. Особенно в разработке. Там все буквально помешаны на time-to-market и делают ради его достижения все, что только можно, лишь бы вывести продукт на рынок побыстрее. Разве что к бабке-шептунье не ходят. В связи с этим спрашивается: а почему, собственно, точно так же нельзя в облаке, где перерасходы – обычное дело и никто не стремится их устранять? Оказывается, можно.</p><h2>Что такое Time-to-Optimize</h2><p>Time-to-Optimize – это временной промежуток между выявлением перерасходов в облаке и их оптимизацией. Она необязательно должна быть полной. Частичная тоже подойдет: уменьшение счетов, высвобождение ресурсов под новые задачи или рост производительности при тех же расходах. Главное, чтобы эти результаты были реальными, а не сводились просто к планам.</p><p>Казалось бы, чего тут вообще считать?</p><p>Дело в том, что перерасход не фиксируется в тот момент, когда его обнаружили. Он не останавливается на месте, а продолжает нарастать как снежный ком. То есть чем выше ваш TTO, тем больше денег утекает в никуда.</p><p>Особенно негативно задержка оптимизации влияет на мультиоблачные среды, когда у каждого провайдера свои правила: свой биллинг, форматы данных, терминология. При такой разносортице попытка свести воедино картину расходов превращается в тот еще квест.</p><p>Со своим железом еще сложнее. Там вообще надо учитывать амортизацию оборудования, зарплаты администраторов, затраты на электричество и обслуживание. И все это нужно как-то аккуратно разложить по проектам и командам.</p><p>В итоге получается замкнутый круг: пока собираете данные, анализируете и согласовываете решения, перерасход продолжает расти. И именно здесь TTO показывает свою ценность: чем быстрее реагируете на проблему, тем меньше денег потеряете.</p><h2>Формула расчета Time-to-Optimize</h2><p>Для расчета Time-to-Optimize есть специальная формула:</p><p>TTO = сбор данных + нормализация + аллокация + анализ + внедрение + фиксация результата</p><p>А вот что значат эти слагаемые:</p><ul><li>Сбор данных — получение сырых биллинговых отчетов от всех провайдеров.</li><li>Нормализация — приведение данных к единому формату.</li><li>Аллокация — распределение расходов по командам, проектам, cost-центрам (кто сколько потратил и на что).</li><li>Анализ — принятие решений о том, что оптимизировать.</li><li>Внедрение — реализация изменений: выключение VM, смена тарифов, резервирование ресурсов.</li><li>Фиксация результата — подтверждение первых измеримых эффектов.</li></ul><p>Опять-таки, на бумаге все выглядит просто. Складываем временные отрезки – получаем итоговое число. Вот только на практике возможны нюансы:</p><p>Прежде всего этапы оптимизации могут идти нелинейно. Вы почти наверняка – особенно при первых попытках оптимизации – будете то останавливаться, то возвращаться к предыдущему шагу, возможно, что-то переделывать и пересчитывать. А иногда и вовсе начинать все заново.</p><p>Другой важный фактор – это длительность каждого этапа, которая зависит от <a href="https://t.me/finops_ru/112">зрелости процессов</a>. Если собирать данные вручную, это займет неделю. С API-интеграциями – считанные минуты. Согласования – наоборот: в стартапе они проходят за пару часов, а корпорации с двадцатью командами могут затянуться даже на неделю.</p><p>Поэтому полезно разделить TTO на уровни:</p><ul><li>Макро-TTO – показывает весь цикл от осознания проблемы до результата, помогает понять общую скорость реакции.</li><li>Микро-TTO – фиксирует длительность отдельных этапов внутри цикла, показывая, как много времени вы тратите на каждый из них.</li></ul><p>Так будет проще выявить проблемные места.</p><h2>TTO как показатель зрелости</h2><p>Time-to-Optimize – штука универсальная. Он показывает не только скорость оптимизации, но и общий уровень зрелости FinOps в компании. <a href="https://habr.com/ru/companies/finops_ru/articles/936768/">Про сами уровни мы уже рассказывали</a>, а вот как они коррелируют со временем оптимизации:</p><ul><li>Crawl (начальный уровень) – когда все делается руками, а на проблемы реагируем постфактум. TTO измеряется 3-6 неделями.</li><li>Walk (средний уровень) – когда уже есть некая системность, частичная автоматизация и FinOps-команда, пусть даже работающая по совместительству. TTO сокращается до 3-7 дней.</li><li>Run (высший уровень) – когда автоматизировано вообще все, используются специализированные FinOps-платформы, а аллокация достигает 100%. TTO – от нескольких часов до пары дней.</li></ul><h2>Примеры расчета Time-to-Optimize</h2><p>Теория теорией, но давайте посмотрим на разницу в результатах в зависимости от уровня зрелости FinOps у одной и той же компании, скажем, с 5 облаками и 10 командами разработки.</p><p>Ручной процесс:</p><ul><li>Сбор данных: 5 дней (походы по консолям, скачивание файлов)</li><li>Нормализация: 3 дня (сведение в Excel)</li><li>Распределение: 4 дня (выяснение владельцев, согласования)</li><li>Анализ и решения: 5 дней (встречи, споры, утверждения)</li><li>Внедрение: 2 дня (технические изменения)</li><li>Результаты: 1 день (новый биллинг)</li></ul><p>Итого: 20 дней</p><p>За это время перерасход продолжает накапливаться, люди отвлекаются от основной работы, и показатели компании падают.</p><p>Автоматизированный процесс:</p><ul><li>Сбор данных: минуты (API все подтягивает сам)</li><li>Нормализация: мгновенно (правила уже настроены)</li><li>Распределение: автоматически (теги работают)</li><li>Анализ и решения: 1 день (система выдает готовые рекомендации)</li><li>Внедрение: 1-2 дня (часть изменений применяется сама)</li><li>Результаты: в реальном времени (дашборды показывают изменения)</li></ul><p>Итого: 2-3 дня</p><p>Разница в десять раз. При этом команды делают то, ради чего их наняли, а не возятся с таблицами.</p><h2>Что замедляет процесс Time-to-Optimize</h2><figure><img src="https://media.tproger.ru/user-uploads/133874/2025-11-06/f9b4dd89-fb16-46fe-8f7e-fd495ede40d4.png" alt="" /></figure><p>Основные факторы замедления TTO</p><p>Ну, вообще-то факторов, влияющих на TTO, довольно много. Но вот самые основные:</p><ul><li>Архитектурная сложность: мультиоблако – это в первую очередь многоформатность, с которой нужно уметь грамотно работать.</li><li>Отсутствие автоматизации: когда вся аналитика делается руками в Excel, удивляться большому TTO не приходится.</li><li>Бюрократия: если вы неделю тратите на то, чтобы согласовать отключение одной забытой виртуалки, вас никакие FinOps-платформы не спасут.</li><li>Плохое тегирование ресурсов: от такой базовой вещи зависит очень многое. А с правильно настроенной системой тегов вся информация о владельцах, проектах и окружениях доступна мгновенно.</li></ul><h2>Польза быстрой оптимизации в FinOps</h2><p>Зачем торопиться, спросите?</p><p>Во-первых, скорость оптимизации напрямую влияет на то, в какую сумму вам обойдется ваша облачная инфраструктура. Если команда тратит лишние 500 тысяч рублей в месяц, при TTO в три недели дополнительный перерасход составит еще 375 тысяч. При TTO в три дня — всего 50 тысяч.</p><p>Во-вторых, большой TTO реально ухудшает рабочий климат. Финдир не может сразу понять, что техническая команда работает, а не просто жжет бюджет, а техлид – не верит, что финансисты помогают развитию, а не тормозят его.</p><p>В-третьих, если оптимизация происходит быстро, это позволяет высвобождать бюджет под новые инициативы здесь и сейчас, а не на будущий месяц. Отключили по-быстрому неиспользуемые виртуалки – и вот вам средства на разработку новой фичи для приложения. Не отключили? Окно возможностей закрылось, и конкуренты выпустили похожий продукт раньше.</p><p>Вывод: в FinOps медленно = плохо.</p><h2>Что тормозит Time-to-Optimize</h2><p>Превращать TTO в самоцель – плохая идея. Ну, какой смысл отключать все подряд ради демонстрации короткого TTO, если через неделю половина системы ляжет, а восстановление обойдется дороже всей потенциальной экономии?</p><p>Кроме того, важно понимать контекст своих действий. Авральная оптимизация из-за двукратного увеличения месячного счета и плановая работа в рамках квартального ревью бюджетов – вообще не одно и то же. Они требуют разной скорости и подхода. Поэтому сравнивать их TTO напрямую некорректно.</p><p>Метрика требует отслеживания дат, этапов, событий. Это дополнительная нагрузка на команду, особенно если у вас параллельно идет несколько инициатив по оптимизации. Как понять, где одна закончилась, другая началась? Чем выше уровень зрелости, тем проще становится, потому что все автоматизировано. Но на начальных стадиях может реально напрягать.</p><p>И последнее. Тип изменений имеет значение. Простые действия типа выключения забытых виртуалок займут пару дней максимум. А архитектурные перестройки могут растянуться на месяцы даже при хорошей автоматизации. Сравнивать эти TTO между собой бессмысленно. Это как мерить скорость марафонца и спринтера одной линейкой.</p><h2>Как использовать TTO на практике</h2><p>Time-to-Optimize можно использовать несколькими способами:</p><ol><li>Как основной KPI FinOps-команды. Забудьте про закрытые тикеты в Jira. Сократили TTO с 20 дней до 5? Значит, эффективность работы выросла в 4 раза. И это ясно всем – от CFO до уборщицы тети Маши.</li><li>Как обоснованиеч бюджетов. Руководство любит цифры, и, если показать ему десятикратную разницу между ручным подходом и автоматизацией. 20 дней против 2 – шутка ли. Вот вам и повод для использования FinOps-платформы. Причем не абстрактное, а с конкретными числами по вашей инфраструктуре.</li><li>Диагностика узких мест. Если разбить TTO на отдельные блоки, то можно сразу увидеть, где все тормозит. Может неожиданно выясниться, что техническая часть работает как часы, а вот согласования тянутся три недели. Тогда уже ясно, с кем надо разговаривать и какие процессы чинить.</li><li>Оценка зрелости процессов. Crawl – это недели, Walk – это дни, Run – это часы. Получается простой и понятный индикатор того, где вы сейчас и куда двигаться дальше.</li></ol><p>В ближайшие годы TTO наверняка станет такой же привычной метрикой, как когда-то стал time-to-market в разработке. Так что компании, которые освоят быстрое реагирование на перерасходы, получат реальное преимущество перед конкурентами.</p><p>Главное — помнить, что TTO это просто инструмент, а не священная корова для поклонения. Его задача помочь выстроить толковую систему управления облачными тратами, где скорость не противоречит здравому смыслу. Потому что лучше потратить лишний день на анализ рисков, чем потом неделю восстанавливать систему и объяснять начальству, что вообще произошло и кто за это ответит.</p><p>Быстрая оптимизация — штука безусловно полезная и прибыльная. Но это метрика должна работать на вас, а не вы на нее.</p>]]></content:encoded>
    </item>
    <item>
      <title>Почему нейросети забывают старое, когда учатся новому? Как ученые пытаются это решить</title>
      <link>https://tproger.ru/articles/pochemu-nejroseti-zabyvayut-staroe--kogda-uchatsya-novomu--kak-uchenye-pytayutsya-eto-rewit</link>
      <comments>https://tproger.ru/articles/pochemu-nejroseti-zabyvayut-staroe--kogda-uchatsya-novomu--kak-uchenye-pytayutsya-eto-rewit?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Валерия Турчак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/pochemu-nejroseti-zabyvayut-staroe--kogda-uchatsya-novomu--kak-uchenye-pytayutsya-eto-rewit</guid>
      <description><![CDATA[<p>О катастрофическом забывании: почему модели теряют навыки и что делать разработчикам</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/pochemu-nejroseti-zabyvayut-staroe--kogda-uchatsya-novomu--kak-uchenye-pytayutsya-eto-rewit">Почему нейросети забывают старое, когда учатся новому? Как ученые пытаются это решить</a>»</p>]]></description>
      <category><![CDATA[Алгоритмы и структуры данных]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[1C]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 26 Nov 2025 12:10:02 GMT</pubDate>
      <content:encoded><![CDATA[<p><i>Эта статья адаптация англоязычного материала The Amnesia Problem: Why Neural Networks Can’t Learn Like Humans (rewire.it)</i></p><h2>Что такое катастрофическое забывание</h2><p>Представьте пианиста, который десять лет играл классику, а потом решил освоить джаз. На первом уроке джаза он вдруг полностью забывает Баха, а уже через неделю больше не помнит простейших гамм.</p><p>В машинном обучении это явление называют <b>катастрофическим забыванием</b>. Нейросеть, когда обучается на новой задаче, стирает знания о старой, и это не сбой конкретной архитектуры или фреймворка — это особенность самого способа обучения нейросетей.</p><h2>Как выглядит забывание на практике</h2><p>Возьмём простую ситуацию. Мы обучаем нейросеть распознавать собак (Задача A) — она показывает 95% точности, потом даём ей новую задачу — классифицировать котов (Задача B). После обучения по новой выборке сеть отлично отличает котов (92%), но если снова проверить её на собаках — точность падает с 95% (как было изначально на собаках) до 10%, <b>то есть до уровня случайного угадывания.</b></p><p>Это не потому, что у сети не хватает параметров. Исследования показывают: ёмкости модели достаточно, чтобы хранить знания по обеим задачам. Проблема в другом — <b>в том, как работает градиентный спуск.</b></p><h2>Почему обучение стирает старое</h2><p><b>Градиентный спуск </b>видит только текущие ошибки и стремится минимизировать функцию потерь именно для новой задачи. То, что было важно для старой, в этот момент не учитывается.</p><p>Во время обучения сеть перемещается по пространству весов, чтобы найти минимум потерь. Для Задачи A она находит подходящее устойчивое решение. Когда приходит Задача B, алгоритм начинает с тех же весов, но дальше шаг за шагом уводит их в новое направление под новую цель. Многие параметры для старой задачи при этом переписываются.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-11-13/3fa86c56-fc84-42d6-a6a4-90fb8f188666.png" alt="" /><figcaption>Рисунок 1: Как меняются параметры сети при обучении на двух задачах. Сначала сеть находит настройки для задачи A (зелёный квадрат), потом начинает учиться задаче B и уходит к новым настройкам (синий квадрат). При этом старые знания стираются.</figcaption></figure><p>В итоге то, что помогало распознавать собак, оказывается теперь непригодным для собак, а подходит только для котов. Алгоритм не учитывает, как теперь работает старая задача — он смотрит только на текущие задачи.</p><h2>Дилемма стабильности и пластичности</h2><p>Чтобы понять, почему катастрофическое забывание вообще возникает, нужно вспомнить о главной проблеме — <b>дилемме стабильности и пластичности. </b>Это правило работает не только для нейросетей, но и для любых систем, которые учатся последовательно.</p><p><b>Пластичность</b> — это способность учиться новому. Когда сеть встречает новую задачу, ей нужно быть гибкой, менять веса и быстро подстраиваться под новые данные.</p><p><b>Стабильность</b> — противоположное качество: умение сохранять старые знания, не давая им разрушиться при каждом обновлении весов.</p><p>Проблема в том, что нельзя увеличить и стабильность, и пластичность одновременно. Если сеть слишком гибкая — она хорошо впитывает новое, но моментально забывает старое. Если наоборот — слишком стабильная, то перестаёт учиться и застывает на месте.</p><p>Это ограничение встроено в саму природу обучения через распределённые параметры. Каждый нейрон обновляет свои веса локально, исходя только из текущей ошибки. И те же механизмы, что помогают сети адаптироваться к новому, одновременно приводят к тому, что старое знание постепенно стирается.</p><h2>Потеря геометрии ландшафта: почему архитектура нужна</h2><p>Чтобы понять, почему одни нейросети подвержены катастрофическому забыванию сильнее других, нужно разобраться в процессе оптимизации. Во время обучения модель корректирует миллионы параметров, минимизируя функцию потерь. Этот процесс можно визуализировать как перемещение по многомерному ландшафту потерь (loss landscape), где каждая точка представляет уникальную конфигурацию весов.</p><p>Ландшафт потерь состоит из областей с разными значениями функции потерь:</p><ul><li><b>Локальные минимумы </b>— области с низким значением потерь, где модель показывает хорошее качество</li><li><b>Высокие области </b>— конфигурации параметров с большой ошибкой</li></ul><p>При обучении на первой задаче (например, классификация собак) модель сходится к одному минимуму. При переходе ко второй задаче (классификация кошек) градиентный спуск направляет модель к совершенно другому минимуму, который может быть далеко от первого. Это приводит к перезаписи весов и потере знаний из предыдущей задачи.</p><p>Критический фактор — геометрия этих минимумов. В одних архитектурах минимумы узкие и острые (sharp minima) — небольшое возмущение параметров резко увеличивает функцию потерь. В других минимумы широкие и плоские (flat minima) — модель более устойчива к изменению весов.</p><p>Исследования показали, что архитектуры с skip connections, например ResNet, как раз формируют такие плоские минимумы — это делает их более устойчивыми к изменению весов, даже когда модель дообучается на новой задаче, её настройки остаются близки к старым, и забывание происходит не так сильно.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-11-13/577b1e05-d21a-4961-8c59-4dcb41ee428b.png" alt="" /><figcaption>Рисунок 2: Сравнение сетей по устойчивости. Обычные сети создают хрупкие решения (слева) — малейшие изменения ломают старые знания. ResNet‑архитектуры (справа) формируют более плавные, устойчивые решения, поэтому забывают меньше при дообучении.</figcaption></figure><p>Это объясняет, почему современные архитектуры забывают меньше старых. У них принципиально другое устройство процесса обучения — более стабильное, которое труднее случайно сломать при добавлении новых задач.</p><h2>Как нейроны конкурируют друг с другом</h2><p>Внутри нейросети каждый нейрон отвечает за определённые признаки, когда сеть обучается распознавать собак, одни нейроны начинают реагировать на уши, другие — на форму морды или лапы. Это и есть специализация — каждый нейрон берёт на себя свою часть работы.</p><p>Но когда мы начинаем дообучать ту же сеть на новой задаче, например на распознавании котов, часть этих же нейронов переиспользуется. Теперь они начинают подстраиваться под морды котов, их уши и хвосты. Постепенно сеть перенастраивает внутренние связи так, чтобы быть полезной для новой задачи, и старые навыки теряются.</p><p>Нейронам не могут одинаково хорошо специализироваться и на собаках, и на котах. Одни и те же группы нейронов работают на разные задачи, из‑за чего старая специализация стирается.</p><p>Исследования показывают, что если две задачи активируют одну и ту же часть нейронов, эффект забывания становится сильнее — до 40–50%. Получается, сеть забывает не из‑за нехватки параметров, а потому что внутренние ресурсы перекрываются. Она просто переучивает старые нейроны на новую роль и не создает новые.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-11-13/2f287dfd-ebc2-493e-8ebf-a212444f507a.png" alt="" /><figcaption>Рисунок 3: Специализация нейронов и конкуренция. Когда задача B активирует 70% тех же нейронов, что и задача A, катастрофическое забывание увеличивается на 45% по сравнению с задачами, где перекрытие составляет всего 30%.</figcaption></figure><h2>Почему повторное обучение не спасает</h2><p>Можно подумать, что проблему легко решить: просто чередовать старую и новую задачу. Сначала немного обучаем сеть на собаках, потом на котах, потом снова на собаках — и так по кругу. Это действительно помогает, но не решает проблему полностью.</p><p>Когда сеть обучается на новой задаче, обновление весов всё равно изменяет параметры, которые нужны для старой. Даже если повторно возвращать старые данные, оптимизатор начнёт переписывать часть старых знаний. Эффект забывания уменьшается, но не исчезает.</p><p>Кроме того, такое обучение становится дорого по ресурсам. Нужно хранить старые выборки, чередовать их с новыми, настраивать порядок подачи данных. Это не подходит для потоковых систем, где задачи появляются одна за другой, или для областей с ограничениями по хранению данных, например, банковская сфера или медицина.</p><h2>Как разработчики пытаются найти баланс</h2><p>На практике <b>дилемма стабильности и пластичности </b>проявляется в выборе параметров обучения. Например, в методе регуляризации разработчики могут настроить, насколько сильно защищать старые данные от изменения.</p><p>Если усилить регуляризацию — старые знания лучше сохранятся, но сеть хуже будет учиться новому. Если ослабить — сеть быстро впитает новую задачу, но забудет старую. Получается постоянный выбор: жертвовать старым или новым.</p><p>Любая точка на этом спектре — компромисс. Нет такого параметра, который позволит и запомнить всё прошлое, и учиться на будущем — на данный момент, это главное ограничение.</p><p>Хуже того, что даже если удаётся сохранить старые знания, появляется другая проблема. После обучения на множестве последовательных задач сеть теряет саму способность учиться. Её нейроны либо перестают реагировать на новые данные, либо повторяют одну и ту же реакцию.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-11-13/1f9d2a7d-cf7a-4062-8b70-813a7a4c3f17.png" alt="" /><figcaption>Рисунок 4: Компромисс между устойчивостью и пластичностью. Граница Парето (пунктирная кривая) представляет собой теоретический предел. Разные методы занимают разные позиции: стандартный SGD отдаёт приоритет пластичности, EWC уравновешивает оба фактора, Experience Replay максимизирует устойчивость. Ни один метод не может одновременно максимизировать оба фактора.</figcaption></figure><h2>Методы борьбы с забыванием</h2><p>Исследователи разработали несколько способов снизить катастрофическое забывание. Но ни один из них не решает проблему полностью — у каждого есть свои ограничения.</p><h3>Elastic Weight Consolidation (EWC) — защита важных весов</h3><p>Это метод, который старается определить, какие параметры критичны для предыдущей задачи, и защищает их от сильных изменений. Когда сеть дообучается на новой задаче, алгоритм смотрит: если какой-то параметр был очень важен для собак, его не надо сильно менять при обучении на котах.</p><p>Звучит хорошо, но есть минусы. Вычислять важность параметров дорого по ресурсам, и метод всё равно не гарантирует полного сохранения знаний.</p><h3>Rehearsal — повторение старых примеров</h3><p>Этот способ работает просто: сеть хранит в памяти часть примеров из старой задачи и время от времени подмешивает их при обучении на новой. Это снижает забывание, но требует хранить старые данные, что может быть невозможно из-за ограничений по памяти или приватности.</p><h3>Архитектурные подходы — разные задачи, разные параметры</h3><p>Самый радикальный способ — выделить разные части сети под разные задачи. Тогда обучение одной задачи вообще не трогает другую, потому что используются разные параметры. Забывания нет, но сеть становится огромной — чем больше задач, тем больше параметров.</p><p>HyperMask (2024 год) использует специальные маски, которые позволяют переиспользовать до 85% параметров между задачами, сохраняя изоляцию.</p><h4>Современные гибридные методы</h4><p>CURLoRA — метод, который комбинирует математическое разложение матриц с ограничениями при дообучении. Он показывает хорошие результаты на больших языковых моделях: например, для модели Mistral 7B удалось снизить ухудшение с 53 896 до 5,44 по метрике perplexity. Но такие методы заточены под конкретные модели и не всегда переносятся на другие архитектуры.</p><h3>Пример на практике</h3><p>Простой эксперимент на PyTorch показывает, как это выглядит. Сеть обучается на двух задачах: сначала на A, потом на B. Полный код доступен в <a href="https://rewire.it/blog/the-amnesia-problem-why-neural-networks-cant-learn-like-humans/index.html">оригинале в файле catastrophic-forgetting-demo.py</a>.</p><p><b>Без защиты:</b> точность на старой задаче падает с 93,6% до 53,6% — потеря 40%.</p><p><b>С методом EWC: </b>точность падает с 91,8% до 74,4% — потеря 17,4%, что на 52% лучше. Но на новой задаче EWC тормозит обучение: вместо 95,6% получается только 73,8%.</p><p>Здесь видно главное: в каждом методе нужно выбирать чем жертвовать. Либо памятью, либо вычислением, либо производительностью на новой задаче. Вы не решаете проблему катастрофического забывания полностью, вы только управляете ею.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-11-13/87f517dd-3e18-4591-9edf-e66d69fa9aec.png" alt="" /><figcaption>Рисунок 5: Каждый метод защиты от забывания требует жертв. Регуляризация быстрая, но слабая. Повторение старых примеров эффективнее, но нужна память. Архитектурное разделение не даёт забыть ничего, но увеличивает ёмкость модели.</figcaption></figure><h2>Как катастрофическое забывание проявляется в продакшене</h2><p>В реальных системах катастрофическое забывание выглядит не как внезапный сбой, а как <b>постепенное ухудшение работы — дрейф модели.</b> Модель, обученная на прошлых данных, уверенно работает сегодня, но через пару месяцев начинает ошибаться. Это и есть то же самое забывание, только в замедленном виде.</p><p>Исследования показывают, что 75% компаний сталкиваются с падением качества моделей без постоянного мониторинга.</p><p>Если модель полгода не обновлять, ошибки на новых данных вырастают примерно на треть. А если просто дообучать её на новых данных, часто улучшается работа на свежих примерах, но при этом падает точность на старых — классическая форма забывания.</p><p>В итоге разработчику приходится выбирать из двух зол: <b>либо держать несколько моделей под разные типы данных, либо мириться с тем, что качество со временем падает.</b> Некоторые компании даже полностью переобучают модели каждый месяц, чтобы вернуть стабильность, хотя это дорого.</p><p>Понимание механизма забывания помогает объяснить, почему идея «просто дообучить модель на новых данных» не работает. Это также оправдывает сложные и затратные пайплайны мониторинга, проверки метрик и периодического переобучения.</p><p>Например, в рекомендательных системах забывание снижает точность старых сегментов пользователей на 20–25%. В медицинском AI модели, обновленные на новых пациентах, теряют около 18% точности на старых группах — и это уже риск для качества диагностики.</p><p>Для инженеров это значит одно: катастрофическое забывание — не теоретическая проблема. Это реальный фактор, который нужно учитывать, если вы строите систему, которая учится на живых данных.</p><h2>Новые подходы: не бороться, а обойти проблему</h2><p>Исследователи всё больше приходят к выводу, что катастрофическое забывание нельзя вылечить напрямую. Вместо этого стоит менять сам способ, как модели обучаются и обновляются.</p><p>Сегодня развиваются три основных направления:</p><h3>1. Непрерывное предобучение</h3><p>Модель постоянно обновляет свои знания, когда появляются новые данные, но делает это аккуратно — чередуя новую информацию со старой из кеша. Такой подход сохраняет примерно 90–95% исходных способностей модели, при этом добавляя свежие знания.</p><h3>2. Непрерывное дообучение (fine-tuning)</h3><p>Здесь используется идея модульности: новая задача обучается поверх базовой модели, но сама база остаётся “замороженной”. Получается, что каждая дополнительная функция подключается как надстройка, не ломая основную логику. Это похоже на плагины, где каждая новая задача — отдельный модуль, не влияющий на предыдущие.</p><h3>3. Множество моделей вместо одной</h3><p>Третий подход — вообще отказаться от идеи универсальной модели, которая знает всё. Вместо этого создают ансамбли: набор специализированных моделей, которые работают вместе. Каждая отвечает за свой участок задачи, и таким образом проблема забывания просто не возникает — разные знания не мешают друг другу.</p><p>Все три подхода не устраняют забывание напрямую, но делают его несущественным. По факту вам просто нужны разные модели под разные задачи.</p><p>Если вы никогда не будете обучать одну и ту же модель сначала на задаче A, а затем на задаче B, проблема забывания исчезает.</p><p>Это немного философское наблюдение: не пытаться побороть ограничение, а выстраивать архитектуру вокруг него.</p><h2>Вопросы, которые остаются</h2><h3>Существует ли математический предел, ниже которого забывание уже невозможно снизить?</h3><p>Некоторые исследования указывают на такие пределы, но точные границы пока не найдены.</p><h3>Как выбирать баланс между стабильностью и пластичностью?</h3><p>Сейчас это делается вручную через подбор параметров методом проб и ошибок. Но нет чёткого понимания, как именно рассчитать оптимальный баланс для конкретной задачи.</p><h3>Влияет ли размер модели на забывание?</h3><p>Практика показывает, что большие модели забывают меньше, но почему это происходит и как именно зависит сила забывания от размера — неясно.</p><h3>Можем ли мы достичь теоретического максимума?</h3><p>Даже если знать предел, можно ли разработать процедуру обучения, которая его достигает? Сейчас методы работают примерно на 70% от теоретически возможного оптимума.</p><h3>Как ведут себя большие модели?</h3><p>Ранние данные показывают, что большие модели забывают данные по-другому, чем маленькие сети. Но системного описания этих различий пока нет.</p><p>Эти вопросы нужны, потому что от них зависят архитектурные решения. Если бы мы могли точно рассчитать границу забывания, можно было бы строить системы с запасом прочности. Если бы умели находить оптимальный баланс, не пришлось бы подбирать параметры вручную.</p><h2>Выводы, которые можем сделать</h2><p>Катастрофическое забывание — не техническая ошибка, которую можно исправить очередным алгоритмом. Это фундамент того, как работают распределённые системы обучения.</p><p>Дилемма стабильности и пластичности встроена в саму природу нейросетей. Каждый нейрон обновляет свои веса на основе текущей задачи, и механизма для сохранения прошлого просто нет. То же свойство, что позволяет сети учиться, одновременно делает её уязвимой перед забыванием.</p><p>Это означает, что правильная постановка задачи — не «как победить забывание», а «как управлять им осознанно». В одних случаях важна стабильность — сохранить старые знания любой ценой. В других важна гибкость — быстро учиться новому. Большинство реальных систем требует баланса.</p><p>Для разработчиков, которые работают с продакшен ML, это значит следующее: последовательное обучение сложнее одноразового обучения в принципе, и никакой алгоритм этого не изменит. Системы, которые хорошо справляются с меняющимися данными, не пытаются впихнуть всё в одну модель. Они признают ограничение и строятся вокруг него — с мониторингом, переобучением, специализированными моделями или ансамблями.</p>]]></content:encoded>
    </item>
    <item>
      <title>Почему важно использовать настоящие GSM-IP, а не эмуляции или VPN-заменители</title>
      <link>https://tproger.ru/articles/pochemu-vazhno-ispolzovat-nastoyashhie-gsm-ip--a-ne-emulyacii-ili-vpn-zameniteli</link>
      <comments>https://tproger.ru/articles/pochemu-vazhno-ispolzovat-nastoyashhie-gsm-ip--a-ne-emulyacii-ili-vpn-zameniteli?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Неопознанный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/pochemu-vazhno-ispolzovat-nastoyashhie-gsm-ip--a-ne-emulyacii-ili-vpn-zameniteli</guid>
      <description><![CDATA[<p>Практические чек-листы, фреймворки и кейсы для специалистов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/pochemu-vazhno-ispolzovat-nastoyashhie-gsm-ip--a-ne-emulyacii-ili-vpn-zameniteli">Почему важно использовать настоящие GSM-IP, а не эмуляции или VPN-заменители</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 25 Nov 2025 12:10:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Полное экспертное руководство по тому, почему реальные GSM-IP шлюзы жизненно важны в современных коммуникациях: безопасность, легитимность, качество связи и практические стратегии внедрения. Практические чек-листы, фреймворки и кейсы для специалистов.</p><h2>Введение</h2><p>Мы живём в эпоху, когда мобильная связь стала критически важной инфраструктурой для бизнеса, безопасности и обслуживания клиентов. Современные проекты требуют интеграции SMS, голосовых вызовов и данных с корпоративными системами, а также удалённого управления устройствами. В этой статье мы подробно объясним, почему использование настоящих GSM-IP шлюзов предпочтительнее эмуляций, программных решений и VPN-заменителей. Вы узнаете технические, юридические и операционные аргументы, получите практические рекомендации по выбору и внедрению реальных GSM-IP решений, а также реалистичные чек-листы и готовые фреймворки для оценки поставщиков. Этот материал предназначен для инженеров, IT-директоров, руководителей проектов и архитекторов коммуникационных решений.</p><h2>Основы: фундаментальные концепции</h2><h2>Что такое GSM-IP?</h2><p>GSM-IP — это интеграция мобильной сети GSM с сетями передачи данных по протоколу IP. По сути, это устройство или система, которая подключает реальные SIM-карты в аппаратных слотах к IP-инфраструктуре: SIP, RTP, SMPP и пр. Шлюз принимает и передаёт голос, SMS и данные, используя мобильный оператор как транзитный канал.</p><h2>Отличие от эмуляций и VPN-заменителей</h2><p>Эмуляция GSM — это попытка воспроизвести поведение мобильной сети программно, без использования реальных операторских каналов. VPN-заменители предлагают туннелирование трафика через IP, маскируя источник, но не обеспечивая реальной мобильной идентичности SIM и IMSI. Ключевое различие: в реальных GSM-IP используются настоящие физические SIM и операторские ресурсы, тогда как эмуляции и VPN манипулируют сетевыми уровнями без участия мобильного оператора.</p><h2>Ключевые термины</h2><ul><li>SIM — модуль идентификации абонента, содержащий IMSI и ключи аутентификации.</li><li>IMSI — уникальный идентификатор абонента в мобильной сети.</li><li>SMPP — протокол для передачи SMS между системами.</li><li>SIP — протокол сигнализации для установки голосовых соединений в IP-сетях.</li><li>HLR/HSS — базы подписчиков в сетях GSM/UMTS/LTE.</li></ul><h2>Глубокое погружение: продвинутые аспекты</h2><h2>Аутентификация и безопасность на уровне SIM</h2><p>Реальная SIM обеспечивает аппаратную аутентификацию в сети оператора посредством секретных ключей. Это означает, что наличие SIM гарантирует, что соединение прошло проверку у оператора, и вызовы или SMS идут через законный канал. Эмуляторы не имеют доступа к таким аппаратным секретам, что делает их уязвимыми к блокировке и манипуляциям со стороны операторов.</p><h2>Качество связи и прогноз латентности</h2><p>Прямое использование GSM-каналов обеспечивает предсказуемое качество: задержки, джиттер и потеря пакетов соответствуют мобильной сети. Эмуляции и VPN могут добавлять дополнительную латентность и непредсказуемые скачки качества из-за маршрутизации в публичных IP-сетях, что критично для голосовых сервисов и низколатентных SMS-коммуникаций.</p><h2>Совместимость с IMS/VoLTE</h2><p>С ростом LTE и VoLTE важна совместимость с IMS-инфраструктурой оператора. Настоящие GSM-IP шлюзы обеспечивают корректную обработку SIP и CSFB, поддерживают голос поверх LTE и корректно работают с IMS-базовой сигнатурой. Эмуляторы часто несовместимы или обходят эти механизмы, что приводит к падениям вызовов и ошибкам рутизации.</p><h2>Юридические и регуляторные требования</h2><p>Многие юрисдикции требуют прозрачности источника и маршрутизации телекоммуникаций. Использование реальных SIM гарантирует, что трафик проходит через зарегистрированные каналы оператора, что облегчает соответствие законам и уполномоченным запросам. Эмуляции и VPN-замены создают риски нарушения законов о хранении данных, прослушивании и противодействии мошенничеству.</p><h2>Практический раздел 1: Выбор архитектуры GSM-IP</h2><h2>Теория: варианты архитектуры</h2><p>Существует несколько базовых архитектур: локальные аппаратные шлюзы с физическими SIM, распределённые облачные точки с удалёнными SIM-банками (физическими или eSIM), гибридные модели, и решения с поддержкой IMS. Каждый подход имеет свои преимущества по масштабируемости, управлению и стоимости.</p><h2>Практика: шаги выбора</h2><ol><li>Определите требования по объёму трафика и SLA.</li><li>Оцените юрисдикционные ограничения: местное размещение SIM, регистрация абонентов.</li><li>Решите, нужен ли вам поддержка VoLTE / SMSC / MMS.</li><li>Оцените риски безопасности и требования шифрования.</li><li>Сравните TCO для аппаратных и облачных решений.</li></ol><h2>Пример</h2><p>Компания X требовала массовой рассылки SMS с высоким уровнем доставки. Мы выбрали локальные GSM-IP шлюзы с физическими SIM у трёх операторов для разнооператорного покрытия и отказоустойчивости. Это дало доставку 98.7% в первой волне и сократило жалобы клиентов на спам.</p><h2>Чек-лист выбора</h2><ul><li>Поддержка физических SIM и eSIM.</li><li>Совместимость с вашими SIP/SMPP системами.</li><li>Возможность локальной установки по юрисдикции.</li><li>Мониторинг качества и SLA от поставщика.</li><li>План аварийного переключения на резервные каналы.</li></ul><h2>Практический раздел 2: Внедрение и интеграция GSM-IP</h2><h2>Теория: ключевые интеграционные точки</h2><p>Интеграция подразумевает соединение GSM-IP шлюза с вашими back-end системами через SIP для голоса и SMPP/HTTP для SMS, а также обеспечение аутентификации, шифрования и логирования. Необходимо также предусмотреть мониторинг на уровне сигнала, качества и использования SIM.</p><h2>Практика: пошаговая инструкция внедрения</h2><ol><li>Подготовка инфраструктуры: выделите VLAN и брандмауэрные правила для GSM-IP.</li><li>Прокладка физического подключения: Ethernet, питание и защита от сбоев.</li><li>Установка и конфигурация шлюза: настройка SIP-профилей и SMPP-соединений.</li><li>Встраивание в биллинг и CRM: реалтайм-метрики и отчёты.</li><li>Тестирование через пилотную зону: стресс-тесты трафика и сценариев отказа.</li></ol><h2>Шаблон конфигурации</h2><p>Мы рекомендуем следующую минимальную конфигурацию: VLAN 10 для управления, VLAN 20 для медийного RTP, IPSec туннель для управления, SMPP соединение с учётом аутентификации по IP и логирование на удалённый SIEM. Настроить автоматическое переключение на резервного оператора при наличии падения RTT выше 300 мс в течение 60 секунд.</p><h2>Чек-лист внедрения</h2><ul><li>Изолированные сети для сигнального и медийного трафика.</li><li>Мониторинг качества: MOS/RTT/RTP loss.</li><li>Логи SMPP и SIP в единой системе агрегации.</li><li>Резервирование питания (UPS) и сетевых путей.</li><li>Процедуры обновления прошивки с откатом.</li></ul><h2>Практический раздел 3: Операционная безопасность и управление рисками</h2><h2>Теория: угрозы и модели риска</h2><p>Основные угрозы для GSM-IP систем включают незаконное использование SIM, SIM-cloning (при наличии уязвимости), кражу SIM, атаки на API шлюза, DDoS на SIP и компрометацию учетных данных SMPP. Управление рисками подразумевает защиту на нескольких уровнях: физическая, сетевого периметра, аутентификация и внутренняя сегментация.</p><h2>Практика: стратегия безопасности</h2><ol><li>Физическая защита SIM: использовать защищенные SIM-кассеты и журналирование доступа.</li><li>Ограничение доступа: RBAC для управления шлюзом и ключами.</li><li>Защита сетевого уровня: IPSec/SSL для SIP, белые списки IP для SMPP.</li><li>Мониторинг аномалий: частые непарные вызовы, всплески отправки SMS со SIM.</li><li>Процедуры инцидент-реакции: быстрое блокирование SIM у оператора.</li></ol><h2>Примеры практических правил</h2><p>Ограничьте ежедневный лимит SMS на одну SIM до значения, соответствующего нормальному пользовательскому поведению. Внедрите автоматическое временное замораживание SIM при превышении порога. Используйте 2FA и HSM для хранения ключей доступа к шлюзу.</p><h2>Контрольный чек-лист безопасности</h2><ul><li>Физический учёт SIM и аудит доступа.</li><li>Автотриггеры на аномалии трафика.</li><li>Шифрование конфигураций и секретов.</li><li>Планы восстановления и резервирования.</li><li>Тестирование на проникновение — ежегодно.</li></ul><h2>Практический раздел 4: Оптимизация и масштабирование</h2><h2>Теория: горизонтальное vs вертикальное масштабирование</h2><p>Горизонтальное масштабирование добавляет больше физических шлюзов и SIM-баков, вертикальное — увеличивает мощность и пропускную способность отдельных устройств. Каждый подход имеет ограничения: количество одновременных голосовых каналов на SIM, ограничения SMPP-сессий у оператора, и инфраструктурные ограничения вашей сети.</p><h2>Практика: план масштабирования</h2><ol><li>Оцените пиковые нагрузки и определите порог масштабирования.</li><li>Разработайте архитектуру с автоматическим добавлением шлюзов и балансировкой SIP/SMPP трафика.</li><li>Распределяйте SIM по операторам и географии для равномерного распределения риска.</li><li>Используйте контейнеризированные сервисы для управляемых компонентов (SMPP gateway, monit сервисы).</li></ol><h2>Методы оптимизации</h2><p>Агрегация SMPP-сессий, кэширование маршрутов, использование предиктивного алгоритма распределения запросов между SIM на основе исторической нагрузки и событий. Внедрите throttling и очереди для управления всплесками.</p><h2>Шаблон фреймворка масштабирования</h2><ol><li>Мониторинг: метрики использования SIM, очереди SMPP, MOS.</li><li>Триггеры: порог использования 70% ведёт к автоматической активации резервного шлюза.</li><li>Автоматизация: Ansible/Terraform для развёртывания новых узлов.</li><li>Отчётность: ежедневные отчёты по доставке и качеству.</li></ol><h2>Типичные ошибки: что НЕ нужно делать</h2><p>Мы часто видим повторяющиеся ошибки в проектах:</p><ul><li>Переоценка возможностей эмуляции: многие команды полагают, что программный эмулятор заменит настоящую SIM. Это приводит к проблемам с доставкой и блокировками.</li><li>Недооценка регуляторных требований: размещение SIM в другой юрисдикции без регистрации может привести к запрету услуг и штрафам.</li><li>Отсутствие физической защиты SIM: неучёт физического доступа и отсутствие учёта приводит к кражам и несанкционированному использованию.</li><li>Игнорирование мониторинга качества: отсутствие MOS, RTT и логов приводит к тому, что проблемы ищут слишком поздно.</li><li>Слабая интеграция с операторами: отсутствие официальных каналов и договоров приводит к нестабильной работе и неожиданным блокировкам.</li></ul><p>Чего не следует делать: использовать общедоступные VPN для маскировки источника мобильного трафика, применять массовое клонирование SIM в масштабных проектах, игнорировать законность обработки персональных данных и передачу метаданных.</p><h2>Инструменты и ресурсы</h2><h2>Аппаратные и программные решения</h2><ul><li>Аппаратные GSM-шлюзы от проверенных производителей с поддержкой SIP/SMPP и управлением SIM-банками.</li><li>Платформы управления SIM (SIM management platforms) для инвентаризации и контроля использования.</li><li>SMPP gateway приложения и open-source SIP PBX для интеграции.</li><li>SIEM и мониторинговые решения (Prometheus/Grafana) для метрик качества.</li></ul><h2>Дополнительные ресурсы</h2><p>Документы операторов по интеграции SMPP/SIP, регуляторные гайды по хранению телеком-данных, отраслевые отчёты по защите инфраструктуры связи. Рекомендуем изучать white papers по IMS и VoLTE, актуальные стандарты 3GPP и публикации по безопасности SIM и протоколов мобильной связи.</p><h2>Кейсы и результаты: реальные примеры применения</h2><h2>Кейс 1: Финтех-компания и гарантия доставки OTP</h2><p>Проблема: Финтех-компания испытывала низкую доставку одноразовых паролей (OTP) при массовых рассылках через виртуальные SMS-сервисы. Решение: Переход на собственную сеть GSM-IP с физическими SIM и распределение по трём операторам. Результат: уровень доставки OTP вырос до 99.3%, среднее время доставки снизилось вдвое, количество отказов клиентов уменьшилось на 84%.</p><h2>Кейс 2: Служба охраны и голосовые оповещения</h2><p>Проблема: Служба охраны использовала VoIP через VPN для аварийных оповещений, но при пиковых нагрузках голосовые каналы падали. Решение: Развёрнуто GSM-IP решение для критических исходящих оповещений, с локальным резервированием силовых каналов и отдельными SIM у двух разных операторов. Результат: Надёжность оповещений выросла с 78% до 99.1% в течение суток, время срабатывания сократилось на 37%.</p><h2>Кейс 3: Маркетинговая кампания с высоким требованием к легитимности</h2><p>Проблема: Маркетинговая команда столкнулась с массовыми блокировками из-за использования дешёвых SMS-агрегаторов и эмуляций. Решение: Запуск кампании через реальные GSM-IP шлюзы, регистрация кампаний у операторов и корректная настройка DLR/SMPP. Результат: Снижение блокировок на 92% и повышение открываемости сообщений, что привело к увеличению ROI рекламы.</p><h2>FAQ: глубокие вопросы и ответы</h2><h2>1. Почему оператор может заблокировать эмулятор или VPN-заменитель?</h2><p>Операторы блокируют источники, которые не проходят аутентификацию на уровне SIM или генерируют аномальный трафик. Эмуляции и VPN часто выявляются по несоответствию сигнатур, отсутствию корректной IMSI/IMEI и по необычным шаблонам трафика. Это приводит к автоматическим фильтрам и ручной блокировке.</p><h2>2. Можно ли заменить физические SIM на eSIM?</h2><p>Да, eSIM даёт гибкость и упрощает дистанционное управление, но при этом требуется поддержка операторов и корректное управление профилями. В ряде стран регуляторы строго относятся к удалённой активации SIM-профилей, поэтому важно учитывать юридические ограничения.</p><h2>3. Насколько безопасно хранить ключи и доступы к GSM-IP шлюзу?</h2><p>Крайне важно использовать HSM или защищённые хранилища для секретов, а также реализовать RBAC и аудит доступа. Утечка ключей может привести к компрометации всей платформы и несанкционированной отправке трафика.</p><h2>4. Какие метрики качества стоит отслеживать?</h2><p>Рекомендуем мониторить MOS для голоса, RTT для SIP, RTP loss, процент доставленных SMS, DLR delays и количество отказов на уровне оператора. Также важно отслеживать аномалии: всплески трафика, необычные шаблоны отправки и новые географические паттерны.</p><h2>5. Как снизить риски юридической ответственности при массовых рассылках?</h2><p>Регистрируйте кампании у операторов, используйте легальные opt-in базы абонентов, храните логи подтверждений и согласий, и следуйте требованиям по обработке персональных данных в вашей юрисдикции. Партнёрство с легитимным оператором снижает вероятность претензий.</p><h2>6. Что делать при обнаружении компрометации SIM?</h2><p>Немедленно блокировать SIM у оператора, изолировать шлюз, проигнорировать все очереди, восстановить состояние из последней безопасной копии и провести расследование. Важно иметь SLA и контакты у операторов для срочного блокирования и замены SIM.</p><h2>7. Стоит ли строить собственную инфраструктуру или пользоваться провайдером GSM-IP?</h2><p>Решение зависит от масштаба, требований к контролю и регуляторных условий. Собственная инфраструктура даёт полный контроль и прозрачность, но требует инвестиций. Провайдеры удобны для старта и масштабирования, но могут ограничивать доступ к деталям и вводить риски зависимости.</p><h2>8. Как избежать штрафов за межоператорские рассылки?</h2><p>Соблюдайте правила операторов, используйте корректные маршруты, регистрируйте номера отправителей и избегайте шаблонов, похожих на спам. Настройка обратной связи и обработка жалоб позволяет быстрее реагировать и сохранять легитимность.</p><h2>9. Как влияют 5G и будущие сети на GSM-IP?</h2><p>5G приносит новые возможности — более низкую задержку, большую пропускную способность и расширенные возможности ядра. Однако многие сервисы будут опираться на LTE/VoLTE и совместимость с существующими IMS. GSM-IP придётся эволюционировать для поддержки новых стандартов и обеспечения межоператорской совместимости.</p><h2>10. Можно ли использовать облачные SIM-банки безопасно?</h2><p>Да, при условии, что SIM-физически управляются в безопасных локациях, их доступ контролируется, а юридические аспекты соблюдены. Облачные SIM-банки удобны, но требуют строгой цепочки доверия и аудита.</p><h2>Заключение: резюме и следующие шаги</h2><p>Использование реальных GSM-IP шлюзов — это не только вопрос качества связи. Это вопрос легитимности, безопасности, соответствия регуляторным требованиям и устойчивости бизнеса. Мы рекомендовали архитектуры, практические шаги внедрения, фреймворки масштабирования и стратегии безопасности. Что делать дальше? Сформируйте внутреннюю рабочую группу, оцените текущие каналы связи на соответствие перечисленным критериям, разработайте пилот с реальными GSM-IP шлюзами и запланируйте тестирование на реальные сценарии нагрузки и отказов. Используйте предоставленные чек-листы для аудита поставщиков и внедрения. Вместе с вами мы можем выстроить надёжную, масштабируемую и соответствующую требованиям инфраструктуру мобильной связи.</p><h2>Полезные контрольные вопросы для старта</h2><ul><li>Какие требования к доставке SMS и голосу у нашего проекта?</li><li>Какие регуляторные ограничения применимы в наших целевых регионах?</li><li>Есть ли у нас ресурсы для управления собственной GSM-инфраструктурой?</li><li>Какие KPI по качеству и SLA мы ожидаем от поставщика?</li></ul><p>Если вы готовы, начните с малого пилота, соберите данные и примите решение на основе метрик и реальных результатов. Настоящие GSM-IP решения стоят своих усилий: они дают контроль, стабильность и юридическую прозрачность, которые невозможно обеспечить эмуляциями или VPN-заменителями.</p>]]></content:encoded>
    </item>
    <item>
      <title>Тормозит Postgres: 12 шагов диагностики</title>
      <link>https://tproger.ru/articles/tormozit-postgres--12-wagov-diagnostiki</link>
      <comments>https://tproger.ru/articles/tormozit-postgres--12-wagov-diagnostiki?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Диана Тажетдинова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/tormozit-postgres--12-wagov-diagnostiki</guid>
      <description><![CDATA[<p>Эксперты собрали системный подход из 12 шагов, чтобы найти узкие места и пофиксить проблемы</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/tormozit-postgres--12-wagov-diagnostiki">Тормозит Postgres: 12 шагов диагностики</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 18 Nov 2025 14:10:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Производительность PostgreSQL может проседать постепенно. Сначала запросы зависают и растёт нагрузка растёт, а затем CPU начинает работать на 100%, хотя данных больше не стало. База тормозит, что сказывается на работе всего проекта. Возможных причин много: от неоптимальных запросов до ошибок конфигурации. В таких ситуациях важно не паниковать, а действовать постепенно — от простого к сложному.</p><p>Вместе с экспертами собрали системный подход из 12 шагов, чтобы найти узкие места и пофиксить проблемы.</p><h2>Шаг 1. Проверьте CPU, память и диск</h2><p>Начинаем с базы — проверяем аппаратные и системные ресурсы. Иногда проблема кроется в том, что серверу объективно тяжело. CPU перегружен, память закончилась, диск устал, и PostgreSQL просто не из чего строить производительность. База работает как сениор-разработчик в последней стадии выгорания.</p><p>На что ориентироваться:</p><ul><li>Если CPU постоянно упирается в предел — анализируем активность запросов или рассматриваем масштабирование.</li><li>Если Swap активен — не хватает памяти.</li><li>Если видите высокий iowait или высокий %util диска, то кандидаты: медленное хранилище, случайные чтения, VACUUM, большие запросы.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/114509/2025-11-13/26993566-d258-4ad7-b0c4-906ac404ef7c.jpeg" alt="" /></figure><h2>Шаг 2. Изучите активность в pg_stat_activity</h2><p>Представляет информацию о текущей активности каждого процесса в реальном времени. Благодаря pg_stat_activity можно увидеть, какие запросы сейчас ждут запуска, какие в работе, а какие съедают больше всего ресурсов производительности. Если смотреть сюда регулярно, большинство проблем видно ещё до аварии.</p><p>На что обратить внимание</p><ul><li>долгие транзакции;</li><li>wait_event_type = 'Lock';</li><li>idle in transaction — признак некорректной работы приложения с транзакциями.</li></ul><blockquote>Однажды в проде столкнулись с резким ростом задержек запросов, далее — массовые ошибки <i>canceling statement due to lock timeout</i>. Выяснилось: в ETL начали повсеместно использовать транзакции, в том числе там, где они не нужны. Долгие этапы блокировали частые операции. Перезапуск PostgreSQL помогал до следующего запуска ETL. <br /><br />Решение: идентифицировать и завершить блокирующие процессы, переработать ETL на более короткие транзакции. В дальнейшем — маркировать подобные процессы и мониторить их отдельно.</blockquote><h2>Шаг 3. Найдите медленные запросы в pg_stat_statements</h2><p>Этот модуль отслеживает статистику выполнения сервером всех операторов SQL. Сюда стоит заглянуть, если зависание и торможение повторяются. Сама база может работать нормально, а один или два плохо написанных запроса — нет.</p><p>Что искать:</p><ul><li>запросы, потребляющие больше всего времени;</li><li>случаи, когда чтение идёт с диска, а не из буфера (shared_blks_read &gt;&gt; shared_blks_hit).</li></ul><figure><img src="https://media.tproger.ru/user-uploads/114509/2025-11-13/59a21ae2-2db3-4af4-943e-67f3c9846fe2.jpeg" alt="" /></figure><h2>Шаг 4. Выявите блокировки</h2><p>Ищите ситуации, когда несколько запросов пытаются работать с одними и теми же данными одновременно. Если один процесс держит строку или таблицу, остальные ждут. Тогда база не падает, а просто стоит в очереди.</p><p>Ищем цепочки блокировок. Частые причины:</p><ul><li>долгие транзакции,</li><li>миграции без окон обслуживания,</li><li>запросы без нужных индексов.</li></ul><h2>Шаг 5. Проанализируйте план запроса с EXPLAIN ANALYZE</h2><p>Эта команда сначала выполняет запрос, а затем выводит фактическое число строк и время выполнения, накопленное в каждом узле плана. Так можно найти место, которое съедает больше всего ресурсов. Неэффективный расход времени и памяти происходит, когда база выбирает неоптимальный план, чтобы выполнить запрос.</p><h2>Шаг 6. Проверьте и оптимизируйте индексы</h2><p>Индекс работает как указатель в книге, чтобы быстро искать нужную страницу. Но если поставить закладки везде, то книга станет толстой и неудобной. PostgreSQL ведёт себя так же: без индекса он перебирает строки вручную, а с перегруженной структурой дольше обновляет таблицу и тратит лишнюю память</p><p>Как оптимизировать:</p><ul><li>не создавать индекс на каждый столбец;</li><li>использовать частичные индексы, если данные распределены неравномерно;</li><li>регулярно проводить REINDEX для крупных объектов.</li></ul><h2>Шаг 7. Устраните раздувание таблиц — bloat</h2><p>PostgreSQL не удаляет ненужные данные сразу, а копит «мёртвые» строки. Если их становится слишком много, таблицы разрастаются. Тогда база тратит ресурсы впустую.</p><p>Диагностика:</p><p>Очистка:</p><h2>Шаг 8. Настройте и проверьте autovacuum</h2><p>Этот фоновый процесс нужен в PostgreSQL, чтобы автоматически обслуживать базу. Пока он работает, система в порядке. Если его выключить или не настроить, то накопится мусор, вырастут таблицы, а база замедлится.</p><p>Проверьте эффективность процессов:</p><p>Настройки должны соответствовать нагрузке и объёму данных.</p><blockquote>Если отключить автовакуум или не управлять им, может произойти быстрое раздувание таблиц и резкое падение производительности.</blockquote><figure><img src="https://media.tproger.ru/user-uploads/114509/2025-11-13/e757d893-9ecc-4db6-a811-06226bdbbbb9.jpeg" alt="" /></figure><blockquote>Если <i>n_dead_tup</i> растёт, а <i>autovacuum_count</i> нет — это сигнал будущих проблем с производительностью и ростом <i>bloat</i>. Также важно следить за <i>WAL</i>: резкий рост записи может указывать на массовые обновления, проблемы с репликацией или приближающееся заполнение диска.</blockquote><h2>Шаг 9. Обновите статистику и настройте планировщик</h2><p>PostgreSQL формирует план выполнения запроса на основе статистики о распределении данных в таблицах. Если статистика устарела, оптимизатор может выбрать неэффективный план.</p><p>Регулярно выполняйте команду ANALYZE, чтобы освежить статистику и корректно оценивать операции.</p><p>Учитывайте важные параметры:</p><ul><li>random_page_cost для SSD ≈ 1.1</li><li>effective_cache_size ≈ 50–75% RAM</li></ul><h2>Шаг 10. Контролируйте соединения</h2><p>Каждое активное соединение потребляет ресурсы сервера, включая память и процессы планировщика. Избыточное количество соединений может привести к деградации производительности.</p><p>Текущее состояние соединений можно просмотреть с помощью:</p><p>Чтобы решить проблему, используйте пул соединений — например, через пулер PgBouncer или аналоги. Такие программы перераспределяют нагрузку между клиентами.</p><figure><img src="https://media.tproger.ru/user-uploads/114509/2025-11-13/98526be9-c2f6-4945-8992-a29f2164d12b.jpeg" alt="" /></figure><h2>Шаг 11. Проанализируйте логи</h2><p>PostgreSQL предоставляет гибкую систему протоколирования, чтобы фиксировать длительные операции, ожидания блокировок и создание временных файлов. Для диагностики посмотрите все логи, связанные с производительностью.</p><p>Рекомендуемые параметры в postgresql.conf:</p><h2>Шаг 12. Оптимизируйте конфигурацию</h2><p>Значения по умолчанию предназначены для минимальных нагрузок — для работы на ноутбуке. Они не дают достаточной мощности в производственной среде.</p><p>Измените ключевые параметры конфигурации:</p><ul><li>shared_buffers ~25% RAM</li><li>effective_cache_size ~50–75% RAM</li><li>work_mem 4–16 MB per operation</li><li>checkpoint_timeout 15–30 min</li><li>random_page_cost = 1.1 для SSD</li></ul><blockquote>Многие думают, что 16 мегабайт — это глобальный лимит на соединение. На самом деле это лимит на операцию. Один запрос может использовать <i>work_mem</i> много раз.</blockquote><h2>Качество и целостность данных: типовые проблемы и решения</h2><p>Качество данных — основа устойчивой IT-системы. Проблемы с ними часто приводят к сбоям и некорректной работе приложений. В PostgreSQL есть встроенные механизмы, которые помогают поддерживать надёжность информации.</p><p>Какие ошибки встречаются чаще всего:</p><ul><li>дубли,</li><li>неверные типы,</li><li>конфликтующие или устаревшие значения,</li><li>частичные и повреждённые записи.</li></ul><p>Как поддерживать целостность данных:</p><ul><li>Проводить регулярный аудит и мониторинг качества данных.</li><li>Внедрять автоматические проверки на дубликаты, пропуски и нарушения правил ещё на этапе загрузки данных.</li><li>Использовать резервное копирование и инструменты восстановления после сбоев.</li><li>Повышать осознанность пользователей и разработчиков — качество данных это часть инженерной культуры.</li></ul><p>Контроль качества данных включает также стратегию восстановления. Даже идеально настроенная валидация не спасает от сбоя диска или человеческого фактора. Резервные копии и репликации должны стать частью общей стратегии.</p><p>На прикладном уровне используйте SAVEPOINT, чтобы аккуратно откатывать операции внутри транзакции. На уровне инфраструктуры работает та же логика:</p><ul><li>проверенные бэкапы возвращают к нормальному состоянию,</li></ul><ul><li>репликация сохраняет доступность, если падает основной сервер.</li></ul><p>SAVEPOINT помогает в рамках одной транзакции, бэкапы и репликация — в рамках всей системы.</p><figure><img src="https://media.tproger.ru/user-uploads/114509/2025-11-13/1aa309ba-009d-48fc-8abc-893d3d20bc1b.jpeg" alt="" /></figure><h3>Как проверять входные данные</h3><p><b>Задавайте допустимые форматы и диапазоны.</b> Указывайте, какие значения вы считаете корректными. Пример:</p><ul><li>дата рождения не может быть позже текущего дня;</li><li>возраст — только числа ≥ 18;</li><li>формат email должен содержать @ и домен.</li></ul><p><b>Проверяйте данные на двух уровнях.</b>  Чтобы пользователь увидел ошибку сразу, смотрите на клиенте — например, в форме сбора контактов.</p><p>Чтобы защититься, если приложение пропустило проблему — на сервере.</p><p><b>Используйте ограничения в базе.</b> SQL-ограничения — это тот самый  встроенный механизм надёжности:</p><ul><li>NOT NULL — поле не может быть пустым;</li><li>UNIQUE — значение должно быть уникальным;</li><li>CHECK — значение должно соответствовать условию;</li><li>FOREIGN KEY — связь с другой таблицей;</li><li>триггеры — дополнительная логика проверки.</li></ul><p><b>Давайте понятные сообщения об ошибках.</b> Пользователь должен понимать, что он сделал не так. Например, вместо Error 400 — invalid data напишите: <i>«Возраст должен быть от 18 лет. Введено: 15»</i>.</p><p><b>Актуализируйте правила проверки.</b> Если вчера минимальная сумма заявки была 5 тысяч, а сегодня 10 — проверка должна обновиться.</p><p><b>Отдайте контроль машине.</b> Используйте автоматизированные проверки:</p><ul><li>поиск дублей;</li><li>контроль пустых полей;</li><li>сверка с бизнес-правилами;</li><li>регулярный скрипт на аномалии.</li></ul><p><b>Документируйте и обучайте.</b> Опишите, какие данные нужны, в каком виде и почему. Покажите команде и пользователям. Чем понятнее правила, тем меньше ошибок.</p><p>Идеальный сценарий: регулярные тестируемые бэкапы + репликация с мониторингом лагов.</p><h2>Чек-лист диагностики за 15 минут</h2><ol><li>pg_stat_activity — долгие транзакции, ожидания</li><li>pg_locks — есть ли блокировки</li><li>pg_stat_statements — тяжёлые запросы</li><li>iostat, htop, free — ресурсы железа</li><li>Проверка автовакуума</li><li>Проверка WAL и места на диске</li><li>Анализ логов</li></ol><h3>Ещё 4 неочевидные причины деградации производительности</h3><p>Что делать, если диагностика по стандарту не помогла? Владимир Васильев рассказал нам, где ещё можно найти источник проблем:</p><ul><li>«Тихое» повреждение данных на диске. Дисковая подсистема не сообщала об ошибках: контроллер RAID с кэшем-бутербродом, где батарея отжила своё. Некоторые страницы данных на диске были битые. Postgres при чтении таких страниц не падал, а уходил в бесконечные ожидания I/O, которые выглядели как резкий скачок нагрузки на диск. Вылечили включением zero_damaged_pages на свой страх и риск, с последующим дампом/рестором. Потом, конечно, заменили железо.</li><li>Конфликт расширений. Была история с pg_stat_statements и auto_explain, которые при определённой настройке (совместная загрузка и высокая частота сэмплирования) начинали сильно тормозить из-за конкуренции за локи в общей памяти. Симптом — высокий wait_event типа LWLock для простых запросов.</li><li>Проблема на уровне файловой системы. Очень старый, но запоминающийся случай: файловая система была заполнена почти под завязку, больше 95%. Это привело к драматическому падению производительности, так как механизмам VACUUM и CREATE INDEX не хватало места для создания временных файлов. Симптомы были самые странные, пока не посмотрели на df -h.</li><li>Субтильный баг в версии PostgreSQL. Однажды апгрейд на минорную версию в рамках одного major привёл к деградации конкретного типа запросов с JOIN. Оказалось, баг в планировщике — он неправильно оценивал стоимость при определённом условии. Помог откат или установка enable_&lt;type&gt;scan = off в качестве временного костыля.</li></ul><h2>Выводы</h2><p>Основные источники проблем:</p><ul><li>долгие транзакции и блокировки</li><li>неэффективные планы выполнения</li><li>недостаточная память и I/O-узкие места</li><li>неверные настройки work_mem, autovacuum, max_connections</li><li>игнорирование метрик и логов</li></ul><p>Универсального решения проблем нет. Чтобы понять причину торможения и разобраться с ней, стоит последовательно уточнять контекст.</p><p>Краткий чек-лист диагностики:</p><ul><li>понять, что происходит сейчас,</li><li>найти узкое место,</li><li>проверить спрос со стороны запросов, а не только предложения со стороны конфигурации,</li><li>убедиться, что данные и процессы здоровы.</li></ul><p>Читайте также</p><ul><li><a href="https://tproger.ru/articles/osnovy-postgresql-dlya-nachinayushhih--ot-ustanovki-do-pervyh-zaprosov-250851">Основы PostgreSQL для начинающих: от установки до первых запросов</a></li><li><a href="https://tproger.ru/articles/postgresql-vs--clickhouse-vs--duckdb--kakuyu-opensors-bazu-vybrat-dlya-analitiki-v-2025-godu-">PostgreSQL vs. ClickHouse vs. Duck DB: какую опенсорс базу выбрать для аналитики в 2025 году?</a></li><li><a href="https://tproger.ru/articles/vrednye-sovety-po-rabote-s-bazami-dannyh--ili-kak-rasstroit-dba">Вредные советы по работе с базами данных, или как расстроить DBA</a></li><li><a href="https://tproger.ru/articles/sql-optimizaciya--5-zaprosov--kotorye-lomayut-bazu">SQL-оптимизация: 5 запросов, которые ломают базу</a></li><li><a href="https://tproger.ru/articles/postgresql--chto-nuzhno-znat-o-schyotchike-tranzakcij">PostgreSQL: что нужно знать о счётчике транзакций</a></li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Математика или программирование? Какие знания нужны ML‑специалисту</title>
      <link>https://tproger.ru/articles/matematik-ili-programmist--chto-nuzhno-znat-ml-specialistu</link>
      <comments>https://tproger.ru/articles/matematik-ili-programmist--chto-nuzhno-znat-ml-specialistu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Валерия Турчак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/matematik-ili-programmist--chto-nuzhno-znat-ml-specialistu</guid>
      <description><![CDATA[<p>Интервью про ML: бустинг против нейросетей, работа с LLM, валидация и культура кода в команде</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/matematik-ili-programmist--chto-nuzhno-znat-ml-specialistu">Математика или программирование? Какие знания нужны ML‑специалисту</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Многопоточность]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 12 Nov 2025 14:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><i>Для Tproger поговорили с Александром Сербулом, руководителем ML‑направления в Битрикс24. Он рассказал, как не застрять на старте и почему в ML инженерная культура важнее модных нейросетей. Внутри — о том, когда градиентный бустинг лучше LLM, почему код нужно покрывать тестами, как работать с нехваткой данных и на что смотреть при выборе команды.</i></p><p>Если вы пишете код и начинаете разбираться с большими данными, здесь много идей, которые можно применить прямо сейчас.</p><p>Главный месседж — зрелый ML‑инженер это не коллекционер нейронок, а разработчик, умеющий проверять гипотезы, валидировать результаты и писать поддерживаемый код.</p><h2>Кем должен быть ML‑специалист — математиком или программистом</h2><p>Без математики в ML далеко не уйдёшь, но и без хорошего кода модели не взлетят. Ошибка в формуле или пробел в логике тестов может стоить недели отладки. Поэтому подход должен быть инженерным: писать чисто, добавлять аннотации типов, покрывать код тестами, следить за читаемостью.</p><h2>Сколько математики нужно в ML</h2><p>Без линейной алгебры, матриц, теории вероятностей и дифференцирования не получится даже понимать, что именно делает модель. Эти базовые знания можно освоить за полгода‑год, но важно не просто формулы выучить, а развить интуицию.</p><p>Инженеру нужно чувствовать, как данные проходят через слои сети, что происходит с тензорами и почему параметры меняются именно так. Это не про сложную теорию — скорее про понимание механики. Когда видишь матрицы не как набор чисел, а как систему преобразований, всё становится проще, и дебаг модели перестаёт быть сложной задачей.</p><h2>Как развиваться в ML и не застрять на старте</h2><p>По мнению Александра, главный способ расти в машинном обучении — постоянно разбирать чужие работы. Читать статьи, пробовать воспроизводить их, собирать сети, смотреть, как устроены архитектуры. Не стоит относиться к программированию как к самоцели. Код — инструмент, но смысл в том, чтобы понимать, что делает модель внутри.</p><blockquote>У многих разработчиков подход поверхностный: берут готовое решение, подают данные и ждут результата, но так не появляется понимания.</blockquote><p>Развитие начинается, когда ты сам задаёшь вопросы вроде «почему слой ведёт себя так», «как меняются веса», «что даёт нормализация». Именно через эти вопросы начинается путь к инженеру, а не к пользователю фреймворков и вайбкодингу.</p><h2>Что с большими языковыми моделями</h2><p>Хайп вокруг больших языковых моделей уже выдохся: корпорации гонятся за размерами, а остальные используют их как библиотеку. Это не плохо — эмбеддинги и промты действительно решают прикладные задачи, от резюмирования встреч до автозаполнения CRM.</p><p>Но инженеру важно понимать, что промт не заменяет модель, это просто способ задать контекст, как преднастройка перед задачей. За ней всё тот же набор весов и вероятностных расчётов. Ошибка — считать промты заменой инженерной работы.</p><p>LLM полезны там, где есть конкретный сценарий и метрики: если видно, что саммари экономит время менеджера или классификатор улучшает отклик в CRM, тогда инструмент оправдан.</p><blockquote>В остальных случаях стоит смотреть на ресурсы и задавать вопрос — нужна ли тяжёлая модель, если задачу решает бустинг или простая логистическая регрессия.</blockquote><h2>Какие методы сейчас реально работают</h2><p>По данным Александра, для бизнес‑задач нет универсального решения, но есть набор проверенных инструментов. <b>Если данные категориальные — лучше использовать градиентный бустинг</b>, например CatBoost или XGBoost. Эти методы часто точнее нейросетей и требуют меньше подгонки.</p><p>Хороший результат даёт <b>комбинация моделей.</b> Например, можно взять LLM для генерации эмбеддингов, а дальше подать их в логистическую регрессию или тот же бустинг. Такой гибрид работает быстрее и требует меньше ресурсов, но при этом сохраняет качество.</p><p>Если данных мало или вычислительных мощностей не хватает, помогает <b>байесовский подход.</b> Он хорошо ведёт себя на шумных и несбалансированных выборках, где другие методы не подходят или не работают. Отдельное направление — <b>оптимизация моделей.</b> Уменьшение моделей через compression позволяет запускать их на обычных серверах без потери точности — актуально для компаний без GPU‑кластеров.</p><h2>Когда ML действительно нужен</h2><p>Не любую задачу стоит решать через машинное обучение. Александр объясняет: если алгоритм можно описать правилами, лучше начинать с классики. Но когда формулы не работают — например, при обработке изображений или машинном переводе — подходит ML. Главное, чтобы были данные: сотни или тысячи пар примеров «правильно» и «неправильно». Без них обучение просто не из чего строить.</p><p>Если данных мало, лучше брать байесовские методы или простые классификаторы — они устойчивее и не требуют много ресурсов. То же самое касается бизнес‑кейсов, где результат критичен, а точность нужно проверять.</p><blockquote>Решение в пользу ML — это не вопрос нового стека, а вопрос наличия данных и понимания задачи. Если подход классических алгоритмов решает проблему быстрее и дешевле, значит, выбираем его.</blockquote><h2>Что делать, если данных мало</h2><p>Недостаток данных — это нормальное состояние, полных и сбалансированных выборок почти не бывает. Поэтому разработчику приходится проявлять креатив. Один из способов — генерировать синтетические данные. Например, создавать дополнительные примеры с небольшими изменениями, чтобы модель видела больше разнообразия.</p><blockquote>Полезно искать и дополнительные источники сигналов. Если пользователь бросил корзину в интернет‑магазине, это может быть прокси‑признак будущей покупки. Такие косвенные данные часто помогают вытащить модель на нужный уровень точности.</blockquote><h2>Где ML приносит реальную пользу бизнесу</h2><p>Александр приводит примеры из практики Bitrix24. <b>Один из самых ощутимых по эффекту — скоринг лидов и сделок.</b> Там используется логистическая регрессия и градиентный бустинг. Метод простой, но позволяет заметно повысить точность прогнозов и напрямую влияет на выручку.</p><p><b>Вторая история — классификация запросов техподдержки.</b> Решение построено на основе алгоритма шинглов и простой модели, оно работает стабильно и подходит для разных языков.</p><p>Алгоритм шинглов — это простой способ представить текст для последующей обработки или сравнения без участия сложных моделей. <br /><br /><i>Пример: текст "машина" при длине шингла 3 даёт набор шинглов: "маш", "аши", "шин", "ина". <br /></i><br />Дальше можно сравнивать эти множества и вычислять похожесть, например через коэффициент Жаккара. <br /></p><p>В Bitrix24 метод шинглов помогает системе одинаково понимать обращения пользователей на разных языках или с разными формулировками.</p><h2>Как проверить, что модель действительно работает</h2><p>Без тестирования и валидации никакая модель не считается готовой. <b>Первое, с чего нужно начинать, — сравнение с базовой линией.</b> Самый простой пример — случайный классификатор. Если ваша модель даёт результат не лучше рандома, значит, смысла в ней нет.</p><p><b>Дальше важно понимать метрики: precision, recall, F1 и другие.</b> Они показывают, на сколько точно модель попадает в нужные ответы и сколько ложных срабатываний допускает. Не стоит воспринимать метрики как формальности — от них зависит, будет ли решение полезно для бизнеса.</p><p><b>Ещё один критерий — устойчивость модели. </b>Александр советует проверять, насколько решение лучше простых эвристик, которые уже используются в компании.</p><blockquote>Если ML‑модель не выигрывает по скорости или точности, её не стоит внедрять. Тестировать нужно на реальных данных и регулярно пересматривать результаты, особенно если бизнес‑процессы меняются.</blockquote><h2>Какие инструменты и ресурсы использовать</h2><p>Основная экосистема машинного обучения сегодня крутится вокруг Python, и начинать логично с него. Хороший старт — <a href="https://realpython.com/">realpython.com</a>, где подробно объясняются основы языка и приёмы написания читаемого кода.</p><p>Но нужно развивать инженерные привычки. Александр рекомендует читать книги вроде <b>Effective Java Джошуа Блоха</b>, чтобы прокачивать культуру разработки и понимать многопоточность. Даже если вы работаете с Python, знания из других языков помогают писать лучше и думать о производительности.</p><p>Из библиотек стоит освоить <b>PyTorch или TensorFlow</b> для нейросетей и <b>scikit‑learn для классического ML</b>. При росте нагрузки полезно переписывать критичные части на <b>Java или Rust</b> — это уменьшает задержки и делает систему устойчивее.</p><h2>Как набраться практики и выбрать правильную команду</h2><p>Лучший способ вырасти в ML — попасть туда, где есть реальные данные и сильная инженерная культура.</p><blockquote>Идите в крупные компании вроде Яндекса, ВК, Сбера или X5 — там есть инфраструктура, процессы и опытные коллеги. Просто читать статьи и собирать pet‑проекты недостаточно, если не сталкиваешься с настоящими продуктами и их ограничениями.</blockquote><p><b>Главный признак хорошей команды — наличие тестов, QA и понятного кода.</b> Если в отделе этого нет, нужно уходить, иначе быстро потеряешь профессиональный уровень. ML уже давно не чудо‑технология, а инструмент. И успех зависит не от того, кто первым применил нейросеть, а от того, чья команда умеет держать систему в рабочем состоянии и проверять результаты каждый день.</p><p>В конце хочется спросить: как вы сами учились или учитесь ML — через курсы, проекты или книги? Делитесь своим опытом и мыслями в комментариях — обсудим, какие подходы к обучению и работе в ML работают сегодня.</p>]]></content:encoded>
    </item>
    <item>
      <title>Разработчик написал «Змейку» на ассемблере размером всего 54 байта — она помещается в QR-код</title>
      <link>https://tproger.ru/news/razrabotchik-napisal--zmejku--na-assemblere-razmerom-vsego-54-bajta---ona-pomeshhaetsya-v-qr-kod</link>
      <comments>https://tproger.ru/news/razrabotchik-napisal--zmejku--na-assemblere-razmerom-vsego-54-bajta---ona-pomeshhaetsya-v-qr-kod?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/razrabotchik-napisal--zmejku--na-assemblere-razmerom-vsego-54-bajta---ona-pomeshhaetsya-v-qr-kod</guid>
      <description><![CDATA[<p>Разработчик создал «Змейку» на ассемблере весом 54 байта — игра полностью помещается в один QR-код и запускается даже без ОС</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/razrabotchik-napisal--zmejku--na-assemblere-razmerom-vsego-54-bajta---ona-pomeshhaetsya-v-qr-kod">Разработчик написал «Змейку» на ассемблере размером всего 54 байта — она помещается в QR-код</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Разработка игр]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Язык ассемблера]]></category>
      <category><![CDATA[Процессор]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 23 Oct 2025 06:40:40 GMT</pubDate>
      <content:encoded><![CDATA[<p>Разработчик под ником <b>donno2048</b> поместил легендарную <b>«Змейку»</b> в <b>54 байта</b> машинного кода — настолько мало, что весь исполняемый код помещается в один QR-код.</p><p>Проект <a href="https://github.com/donno2048/snake">опубликован</a> на GitHub и сопровождается демонстрацией, инструкциями и, непосредственно, самим QR-изображением.</p><h2>Как это работает</h2><p>Это x86-код, изначально рассчитанный на DOS. Есть и вариант, который не требует BIOS, загрузчика или операционной системы.</p><p>Автор использует тонкие приемы оптимизации — минимальные инструкции, хитрые трюки с регистрами и адресацией. И все это нужно чтобы уместить игровую логику и отрисовку в крошечный набор байтов.</p><figure><img src="https://media.tproger.ru/user-uploads/111041/2025-10-23/89cbac88-e7fb-48a5-b5ef-323708eed671.jpeg" alt="" /></figure><h2>Зачем все это</h2><p>Разумеется, это никакая не попытка заменить полноценную игру, а упражнение в минимализме: показать, как далеко можно зайти с низкоуровневыми оптимизациями.</p><p>В README автор прямо сравнивает размер в байтах с обычными программами.</p><h2>Практическое значение и эстетика</h2><p>Проекты вроде этого интересны двумя вещами:</p><ol><li><b>Техническая демонстрация</b> — они учат «компактному» мышлению и глубокому пониманию архитектуры процессора;</li><li><b>Эстетика хакерской культуры</b> — умение поместить игру в QR-код служит чистым челленджем и поводом для обсуждений в сообществе.</li></ol><h2>Где посмотреть и запустить</h2><p>Сборник кода, QR-изображение и онлайн-демо доступны на <a href="https://github.com/donno2048/snake">странице проекта</a> на GitHub.</p><p>Там же — инструкции, как запустить локально (скрипты main.sh/main.bat) и как собрать свое QR-воплощение.</p>]]></content:encoded>
    </item>
    <item>
      <title>SQL-оптимизация: 5 запросов, которые ломают базу</title>
      <link>https://tproger.ru/articles/sql-optimizaciya--5-zaprosov--kotorye-lomayut-bazu</link>
      <comments>https://tproger.ru/articles/sql-optimizaciya--5-zaprosov--kotorye-lomayut-bazu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/sql-optimizaciya--5-zaprosov--kotorye-lomayut-bazu</guid>
      <description><![CDATA[<p>Типовые SQL-запросы, которые тормозят базу: от JOIN без условий до DISTINCT в лоб. Почему они опасны и как их переписать правильно, чтобы база работала быстро и стабильно.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/sql-optimizaciya--5-zaprosov--kotorye-lomayut-bazu">SQL-оптимизация: 5 запросов, которые ломают базу</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 15 Oct 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Оптимизация SQL-запросов — это набор вполне конкретных правил, нарушение которых может положить любую базу. Даже один неудачный JOIN или ORDER BY без индекса способны превратить быструю систему в тормозящую. Мы собрали типовые запросы, которые чаще всего убивают производительность, объяснили, почему они опасны, и показали, как переписать их правильно.</p><h2>1. Запросы без ограничений</h2><p>Запрос:</p><h3>Почему он опасен</h3><p>Такие запросы тянут абсолютно все данные из таблицы, даже если нужны 2–3 поля. На больших таблицах это превращается в тяжелую нагрузку на сервер: растет объём передаваемых данных, замедляется отклик, падает производительность. Если запрос используют в проде (например, при каждом открытии страницы), он легко может «задушить» базу под пиковыми нагрузками.</p><h3>Как переписать правильно</h3><p>или</p><p>Важно явно указывать необходимые поля и добавлять LIMIT, если не нужны все строки сразу. Это снижает нагрузку и ускоряет ответ.</p><h2>2. WHERE без индексов</h2><p>Запрос:</p><h3>Почему он опасен</h3><p>Если по полю status нет индекса, база будет проходить всю таблицу построчно (full table scan), чтобы найти нужные строки. На таблицах с миллионами записей это приводит к заметной деградации производительности, особенно при частых запросах. Если таких фильтров несколько, нагрузка на сервер растет лавинообразно.</p><h3>Как переписать правильно</h3><p>Добавить индекс по фильтруемому полю:</p><p>Переписать запрос, чтобы он использовал индекс:</p><p>Если поле часто используется в фильтрах или джоинах — индексировать его почти всегда оправдано. Это резко ускоряет поиск и снижает нагрузку.</p><h2>3. GROUP BY и агрегаты по большим таблицам</h2><p>Запрос:</p><h3>Почему он опасен</h3><p>GROUP BY заставляет базу данных сначала обработать все строки, а потом сгруппировать их в памяти или на диске. Если таблица содержит миллионы записей и нет подходящих индексов, такой запрос приводит к сортировке или хэшированию огромных объёмов данных. Итог — долгие выполнения, рост потребления оперативной памяти и, в худшем случае, временные таблицы на диске (disk spill). Дополнительную нагрузку дают агрегаты (COUNT, SUM, AVG и др.) без фильтров — база пересчитывает их для всех строк, даже если вам нужна малая часть.</p><h3>Как переписать правильно</h3><p>Ограничить выборку с помощью фильтров до агрегации:</p><p>Добавить индекс по ключу группировки:</p><p>Если данные статичны — использовать материализованные представления или промежуточные агрегации, чтобы не пересчитывать заново каждый раз.</p><p>GROUP BY хорошо работает на подготовленных и отфильтрованных данных. Если он упирается в «сырые» миллионы строк — это сигнал к пересмотру логики запроса.</p><h2>4. Подзапросы в WHERE (особенно некоррелированные)</h2><p>Запрос</p><h3>Почему он опасен</h3><p>Подзапрос в WHERE нередко превращается в лишний проход по таблице. Если оптимизатор не умеет эффективно преобразовать подзапрос в JOIN, он будет сначала выполнять подзапрос (часто без индексов), а потом сверять результаты с основной таблицей. При больших объёмах это означает десятки тысяч сравнений и скачки производительности. Особенно опасны некоррелированные подзапросы, которые не зависят от внешнего запроса — они могут выполняться повторно или создавать временные таблицы.</p><h3>Как переписать правильно</h3><p>Заменить подзапрос на JOIN:</p><p>Убедиться, что по ключам соединения и фильтрации (customer_id, country) есть индексы:</p><p>Если подзапрос всё же нужен — использовать EXISTS, а не IN, чтобы оптимизатор мог «коротко замыкать» проверку:</p><p>Подзапросы — удобный синтаксис, но не всегда эффективный. JOIN и индексы почти всегда работают быстрее, особенно на больших таблицах.</p><h2>5. ORDER BY без индекса</h2><p>Запрос</p><h3>Почему он опасен</h3><p>Когда вы используете ORDER BY по колонке без индекса, база вынуждена сортировать всю выборку в памяти или на диске. Это означает дополнительную нагрузку на CPU, а при больших объёмах данных — полную деградацию производительности. Часто такие запросы встречаются в продакшене в виде пагинации по миллионам строк. В итоге простой «листинг заказов» превращается в многосекундный или даже минутный ответ.</p><h3>Как переписать правильно</h3><p>Добавить индекс по полю сортировки:</p><p>Ограничить выборку с помощью LIMIT и пагинации:</p><p>Использовать составные индексы, если сортировка идёт вместе с фильтром:</p><p>А какие запросы убивают вашу базу? пишите в комментариях!</p>]]></content:encoded>
    </item>
    <item>
      <title>Вышел Python 3.14. Что нового и насколько он стал быстрее?</title>
      <link>https://tproger.ru/news/vywel-python-3-14---naskolko-on-bystree-</link>
      <comments>https://tproger.ru/news/vywel-python-3-14---naskolko-on-bystree-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Валерия Турчак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/vywel-python-3-14---naskolko-on-bystree-</guid>
      <description><![CDATA[<p>Python 3.14 стал быстрее на 27%, получил free-threading без GIL и впервые полноценно раскрывает многопоточность для современных процессоров</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/vywel-python-3-14---naskolko-on-bystree-">Вышел Python 3.14. Что нового и насколько он стал быстрее?</a>»</p>]]></description>
      <category><![CDATA[Многопоточность]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Процессор]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 09 Oct 2025 09:17:31 GMT</pubDate>
      <content:encoded><![CDATA[<p><b>Python 3.14</b> вышел совсем недавно — 7 октября. А уже на следующий день разработчик Мигель Гринберг опубликовал результаты независимых тестов.</p><p>Главный итог — новая версия работает <b>примерно на 27% быстрее</b>, чем Python 3.13. Ключевое новшество — полноценная поддержка <b>free-threading</b> (многопоточности без глобальной блокировки GIL).</p><h2>Как тестировали</h2><p>В тестах участвовали версии CPython 3.9–3.14, а также PyPy 3.11, Node.js 24 и Rust 1.9.</p><p>Проверяли производительность на двух алгоритмах: рекурсивном вычислении чисел Фибоначчи и сортировке пузырьком. В однопоточном режиме Python 3.14 показал стабильный прогресс:</p><ul><li>В тесте Фибоначчи ускорение на <b>27%</b> — 6,4 секунды против 8,2 секунд у версии 3.13.</li><li>В сортировке пузырьком время сократилось <b>до 2,05 секунды</b> против <b>2,8 секунд</b>.</li></ul><h2>Революция free-threading</h2><p>Главный прорыв — <b>free-threading</b>, который наконец снимает системное ограничение GIL и раскрывает потенциал многоядерных процессоров.</p><p>В четырехпоточном тесте Фибоначчи скорость выросла <b>в три раза</b>, а в сортировке — <b>в два раза</b> относительно стандартной сборки.</p><h2>Сравнение с другими</h2><p>Стоит отметить, что PyPy 3.11 остается недосягаемым лидером — он быстрее CPython 3.14 почти в пять раз в рекурсии и в 18 раз при сортировке.</p><p>Node.js приблизился к PyPy в одном тесте, а Rust ожидаемо вырвался вперед — до 70 раз быстрее Python.</p><h2>Что это значит</h2><p>Python 3.14 — самая быстрая версия CPython на сегодня. Для проектов с интенсивными вычислениями free-threading дает ощутимый прирост. А вот JIT-режим пока остается экспериментальным — ускорения почти нет.</p><p>Если ваша команда может обновиться — <b>это стоит сделать</b>. Python 3.14 не просто быстрее: он впервые по-настоящему раскрывает многопоточность.</p>]]></content:encoded>
    </item>
    <item>
      <title>GPU в бизнесе: как считать окупаемость инвестиций в вычислительную мощь</title>
      <link>https://tproger.ru/articles/gpu-v-biznese--kak-schitat-okupaemost-investicij-v-vychislitelnuyu-moshh</link>
      <comments>https://tproger.ru/articles/gpu-v-biznese--kak-schitat-okupaemost-investicij-v-vychislitelnuyu-moshh?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Филатов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/gpu-v-biznese--kak-schitat-okupaemost-investicij-v-vychislitelnuyu-moshh</guid>
      <description><![CDATA[<p>GPU для бизнеса: окупаемость и выбор стратегии. Расчет ROI, сравнение CAPEX vs OPEX для AI, рендеринга и HPC. Аренда мощностей NVIDIA V100/P100.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/gpu-v-biznese--kak-schitat-okupaemost-investicij-v-vychislitelnuyu-moshh">GPU в бизнесе: как считать окупаемость инвестиций в вычислительную мощь</a>»</p>]]></description>
      <category><![CDATA[Масштабируемость и ограничения памяти]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 04 Oct 2025 12:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Бывает, команда data scientists ждёт результатов обучения модели несколько дней. А значит, мощности простаивают, идеи себя не оправдывают и всё это выливается в упущенную выгоду. Однако процесс можно сократить до нескольких часов. Звучит как магия? Нет — это реальность, которую предлагают современные графические процессоры, GPU. Но когда эта мощь перестаёт быть дорогой игрушкой и начинает приносить измеримую прибыль?</p><p>Пора перестать смотреть на GPU как на экзотику для геймеров и IT-гигантов. Сегодня это — стратегический актив. Он существенно ускорит ключевые бизнес-процессы. Вопрос лишь в том, как грамотно подойти к вложениям и не слить бюджет.</p><h2>Тревожные звоночки: пора покупать GPU?</h2><p>Как понять, что ваш бизнес достиг той точки, когда мощности центральных процессоров стали тормозом? Есть несколько явных сигналов.</p><p>Если рендеринг видео или сложной 3D-графики растягивается на ночь, заставляя дизайнеров и инженеров простаивать в ожидании. Если модели машинного обучения обучаются неделями, не позволяя оперативно тестировать гипотезы и улучшать продукт. Если анализ больших данных или сложные финансовые симуляции не дают инсайтов для принятия решений. Это не просто технические неудобства — это прямые потери денег и конкурентных позиций. Компании, которые уже используют GPU-ускорение, попросту двигаются быстрее.</p><h3>Где GPU приносят реальные деньги</h3><p>Мощность сама по себе бессмысленна. Ценность появляется, когда она решает конкретные бизнес-задачи.</p><p>Возьмём, к примеру, искусственный интеллект. Скорость здесь — это всё. Сокращение времени обучения модели с десяти дней до одного означает не просто экономию времени. Это возможность запустить на 30% больше экспериментов и быстрее выйти на рынок с уникальным продуктом. Выгода становится очевидна.</p><p>В индустрии дизайна и медиа GPU творят чудеса. Студия, сократившая время рендера кадра с часа до пяти минут, увеличивает свою пропускную способность в разы. Это позволяет брать больше проектов без расширения штата, повышать качество картинки и соблюдать самые сжатые сроки. Здесь окупаемость считается в дополнительных контрактах и лояльности клиентов.</p><h2>CAPEX или OPEX: финансовая развилка</h2><p>Итак, решение принято. Но как поступить — построить собственную инфраструктуру или воспользоваться облачной арендой? Это фундаментальный выбор между двумя финансовыми моделями.</p><p><b>Покупать свои GPU (CAPEX).</b> Это стратегия основателя крепости. Вы владеете всем: от процессора до системы охлаждения. Плюсы? Полная независимость и предсказуемые расходы в будущем. Минусы? Огромный первоначальный взнос, а техника имеет привычку устаревать буквально на глазах. Плюс к этому — штат инженеров для ее содержания. Выбор для тех, у кого работа кипит 24/7 и планы расписаны на годы вперед.</p><p><b>Арендовать в облаке (OPEX)</b>. Это стратегия мобильной команды. Нужны мощности — включил. Закончил проект — выключил и перестал платить. Главный козырь — невероятная гибкость. Сегодня тестируете модель на одной видеокарте, а завтра для нового клиента разворачиваете целый кластер за считанные минуты.</p><h2>Считаем выгоду: простая формула ROI</h2><p>Чтобы принять взвешенное решение, необходим хотя бы примерный расчет возврата на инвестиции. Формула выглядит пугающе только на первый взгляд.</p><p>Выгода (₽) = (Сэкономленное время сотрудников × почасовую ставку) + (Ускорение вывода продукта × потенциальный доход) + Снижение операционных издержек.</p><p>Затраты (₽) = Стоимость оборудования / аренды + Электроэнергия + Обслуживание.</p><p>ROI (%) = ((Доход – Затраты) / Затраты) × 100.</p><p>Рассмотрим гипотетический пример. Допустим, инженерная команда тратила на симуляции 200 часов в месяц. Внедрение GPU сократило это время до 20 часов. При стоимости часа работы инженера в $50 экономия на фонде оплаты труда составляет $9000 в месяц. Если аренда необходимых GPU-мощностей обходится в $4000, ежемесячная чистая выгода — $5000. Простой ROI в этом случае превышает 120%. Цифры условны, но они наглядно демонстрируют, что при правильном подходе инвестиции могут окупаться очень быстро.</p><h2>Резюмируя: шаг к осознанным инвестициям</h2><p>GPU давно перестали быть узкопрофильной технологией. Они превратились в мощный бизнес-инструмент и дают конкурентное преимущество через скорость. Инвестиции в них оправданы. Но важно чётко понимать, какие процессы ускорятся и как это повлияет на финансовые результаты.</p><p>Выбирайте строительство собственного кластера (CAPEX) для стабильных, непрерывных рабочих нагрузок. Аренда в облаке (OPEX) — более гибкий и менее рискованный путь для старта и тестирования идей. Начните с пилотного проекта, соберите данные и посчитайте. Возможно, ваша главная победа над конкурентами лежит не в найме еще одного сотрудника, а в покупке или аренде нескольких видеокарт.</p><h2>Чек-лист для принятия решения:</h2><ul><li>Выявлен бизнес-процесс, который тормозит развитие (AI, рендеринг, симуляции).</li><li>Измерены текущие временные и финансовые потери.</li><li>Оценена потенциальная выгода от ускорения.</li><li>Проанализирован характер нагрузки (постоянная/пиковая).</li><li>Проведено сравнение CAPEX и OPEX на основе реалистичных расчетов ROI.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>«SQL хорош для данных, но плох для логики» — почему все больше разработчиков выносят бизнес-логику из базы</title>
      <link>https://tproger.ru/news/-sql-horow-dlya-dannyh--no-ploh-dlya-logiki----pochemu-vse-bolwe-razrabotchikov-vynosyat-biznes-logiku-iz-bazy</link>
      <comments>https://tproger.ru/news/-sql-horow-dlya-dannyh--no-ploh-dlya-logiki----pochemu-vse-bolwe-razrabotchikov-vynosyat-biznes-logiku-iz-bazy?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/-sql-horow-dlya-dannyh--no-ploh-dlya-logiki----pochemu-vse-bolwe-razrabotchikov-vynosyat-biznes-logiku-iz-bazy</guid>
      <description><![CDATA[<p>SQL отлично справляется с данными, но неудобен для бизнес-логики: разработчики выносят её в код ради гибкости, скорости и независимости</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/-sql-horow-dlya-dannyh--no-ploh-dlya-logiki----pochemu-vse-bolwe-razrabotchikov-vynosyat-biznes-logiku-iz-bazy">«SQL хорош для данных, но плох для логики» — почему все больше разработчиков выносят бизнес-логику из базы</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[C#]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Oracle]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 24 Sep 2025 11:06:25 GMT</pubDate>
      <content:encoded><![CDATA[<p>Современные приложения все чаще используют базы данных исключительно как «хранилище», а не как движок для бизнес-логики. <i>Почему? </i></p><p>Потому что SQL, несмотря на свое превосходство в работе с данными, неудобен, ограничен и рискован в роли полноценного языка программирования.</p><h2>Логика не по адресу</h2><p>Как <a href="https://ewaldbenes.com/en/blog/why-i-keep-business-logic-out-of-sql">отмечает</a> Эвальд Бенс, фуллстек разработчик и авторов популярного блога об IT, он <b>предпочитает держать как можно больше логики в приложении</b>, а не в SQL-хранилищах, представлениях и процедурах:</p><blockquote>Я отношусь к базе как к тупому хранилищу данных. Вся логика — в коде. SQL просто не предназначен для сложных сценариев.</blockquote><p>И дело не в том, что SQL чего-то «не умеет». Умеет — особенно с расширениями вроде PL/pgSQL или Oracle PL/SQL.</p><p><b>Но выразительность этих языков — далека от Python, TypeScript, Rust или C#</b>, особенно когда речь идет о бизнес-логике, объектной модели или сложных расчетах.</p><h2>Зачем выносить логику из базы?</h2><h2>1. SQL невыразителен</h2><p>Для манипуляций с табличками и JOIN — он король. Но как только начинается рекурсия, классы, вложенные состояния или обработка ошибок — SQL превращается в монстра с BEGIN, IF, LOOP, EXCEPTION, RAISE, CURSOR, FETCH, WHILE и т.д...</p><h2>2. Развертывание — боль</h2><p>Обновить приложение можно за минуту, откатить — за две. А вот миграции на проде — это:</p><ul><li>повышенные права доступа;</li><li>согласование с DBA;</li><li>боязнь сломать что-то «наживую»;</li><li>ручной откат схем.</li></ul><h2>3. Лочит на вендора</h2><p>Логика, написанная на PL/SQL — это билет в один конец к Oracle. Переезд на PostgreSQL или SQL Server становится в 5 раз сложнее.</p><h2>4. Меньше инструментов</h2><p>Вокруг SQL есть утилиты, но полноценной среды с юнит-тестами, статическим анализом, форматтерами, профайлерами, линтерами и отладчиками — почти нет.</p><h2>А что с производительностью?</h2><p>Да, есть нюанс: <b>иногда лучше обработать данные прямо в базе</b>, чтобы не гонять десятки тысяч строк по сети. Это особенно актуально, если не хочется получить классическую проблему N+1-запросов.</p><p>Но даже в этом случае большинство специалистов советует <b>сначала делать «просто и понятно», а не «оптимально заранее»</b>. До тех пор, пока не уперлись в реальные метрики, выносить логику в базу — преждевременная оптимизация.</p><h2>Что делать?</h2><p>Подход <i>«база — хранилище, логика — в коде»</i> стал де-факто стандартом во многих командах. Но это не догма:</p><ul><li>Для отчетности или простых ETL сценариев логика в SQL может быть уместной.</li><li>Для монолитных решений без CI/CD и DevOps — процедурный SQL вполне оправдан.</li><li>Но в современной разработке с частыми релизами, микросервисами и отказоустойчивостью <b>бизнес-логика в приложении — просто удобнее и безопаснее</b>.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>5 сервисов для мониторинга всех метрик инфраструктуры</title>
      <link>https://tproger.ru/articles/5-servisov-dlya-monitoringa-vseh-metrik-infrastruktury</link>
      <comments>https://tproger.ru/articles/5-servisov-dlya-monitoringa-vseh-metrik-infrastruktury?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/5-servisov-dlya-monitoringa-vseh-metrik-infrastruktury</guid>
      <description><![CDATA[<p>Подборка сервисов для мониторинга метрик инфраструктуры: инструменты, которые позволяют отслеживать состояние систем в реальном времени и предотвращать сбои.
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/5-servisov-dlya-monitoringa-vseh-metrik-infrastruktury">5 сервисов для мониторинга всех метрик инфраструктуры</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Oracle]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Grafana]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 19 Sep 2025 12:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Инфраструктура редко падает внезапно — почти всегда система заранее подает сигналы: растет нагрузка, замедляются запросы, перегреваются ресурсы. Чтобы не ловить проблемы по факту, а управлять ими заранее, нужны сервисы, которые собирают и визуализируют метрики в реальном времени. В этой подборке мы собрали инструменты, которые помогают держать руку на пульсе всей инфраструктуры и принимать решения на основе данных, а не догадок.</p><h2>1. 10-Страйк: Мониторинг Сети Pro</h2><p><a href="https://www.10-strike.ru/network-monitor/">10-Страйк: Мониторинг Сети Pro</a> — это российская программа для системных администраторов, которая позволяет контролировать состояние сетевых устройств, серверов, рабочих станций, коммутаторов, баз данных и других ресурсов инфраструктуры. Она отслеживает ключевые параметры — от свободного места на дисках и загрузки процессора до температуры оборудования — и в случае проблем отправляет уведомления по email, SMS или в мессенджеры. Система может не только сигнализировать о сбоях, но и автоматически устранять их, например, перезапуская службы или выполняя скрипты.</p><h3>Технические возможности</h3><p>Продукт поддерживает десятки видов сетевых проверок через ICMP, SNMP, HTTP, SQL, SSH и другие протоколы. Возможно мониторить серверы Windows и Linux, сетевые службы, видеокамеры, принтеры, СУБД и промышленное оборудование. Визуализация данных доступна на карте сети с графиками и индикаторами. В версии Pro предусмотрен распределённый мониторинг с несколькими серверами и аген­тами, а также веб-интерфейс для удалённого управления.</p><h3>Сценарии использования</h3><p>Для DevOps и системных администраторов 10-Страйк подходит как инструмент централизованного мониторинга с алертами и картой сети. IT-отделы предприятий используют его для контроля доступности каналов связи, серверов, баз данных и устройств. Руководители могут формировать отчёты по аптайму и SLA для анализа стабильности работы инфраструктуры.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-09-18/5654eec1-f0ae-4170-aa26-922420d13f4e.png" alt="" /></figure><h3>Особенности</h3><p>Программа выделяется простотой настройки проверок, наличием наглядной карты сети и гибкой системой сигнализации. Важное преимущество — возможность распределённого мониторинга в удалённых сетях и работы в круглосуточном режиме без участия администратора. Решение разработано в России и подходит под задачи импортозамещения.</p><h3>Тарифы и условия</h3><p>Продукт распространяется по лицензии с ограничением на число сенсоров: версия Pro на 100 сенсоров стоит 40 000 рублей, стандартная версия — 20 000 рублей. Для корпоративных лицензий действует ограничение на количество серверов мониторинга.</p><h2>2. Deckhouse Prom++</h2><p><a href="https://deckhouse.ru/products/prompp/?utm_source=web&amp;utm_medium=tproger&amp;utm_campaign=monitoring">Deckhouse Prom++ </a>— это Open Source-система мониторинга на базе Prometheus, которая потребляет до 10 раз меньше памяти. Она собирает метрики приложений, сервисов и инфраструктуры в реальном времени, хранит их во встроенной TSDB и поддерживает PromQL для анализа. Deckhouse Prom++ умеет формировать алерты и легко интегрируется с Grafana, оставаясь привычным для команд, которые уже работают с Prometheus.</p><h3>Технические возможности</h3><p>Deckhouse Prom++ может собирать любые инфраструктурные метрики напрямую или через экспортеры: состояние серверов, контейнеров, сетей, баз данных и приложений. Главная оптимизация по сравнению с «ванильным» Prometheus — переработка Write Ahead Log — позволяет снизить потребление памяти без ущерба производительности. Продукт полностью совместим с API и настройками Prometheus: дашборды, правила алертинга и интеграции продолжают работать без изменений. Prom++ уже используется на более чем 1000 кластеров и поддерживает до 10 млн активных метрик на кластер.</p><h3>Сценарии использования</h3><p>DevOps-инженеры и SRE могут использовать Deckhouse Prom++ для мониторинга Kubernetes и традиционной инфраструктуры. Архитекторы и CTO получают возможность сократить расходы на RAM без отказа от привычной экосистемы. Prom++ подходит и для on-prem, и для облачных окружений, а также уже встроен в Deckhouse Kubernetes Platform.</p><h3>Особенности</h3><p>Ключевое преимущество Deckhouse Prom++ — низкое потребление ресурсов: до 10 раз меньше памяти, чем у Prometheus, и до 3 раз меньше, чем у VictoriaMetrics. Сервис полностью совместим с экосистемой Prometheus, не создаёт вендорлока и распространяется под лицензией Apache 2.0. Поддержка осуществляется инженерами Deckhouse и сообществом через открытый Telegram-чат.</p><h3>Тарифы и условия</h3><p>Deckhouse Prom++ — полностью бесплатный Open Source-продукт. Ограничений по количеству пользователей или метрик нет.</p><p>Есть <a href="https://github.com/deckhouse/prompp/?tab=readme-ov-file#migrating-from-prometheus">инструкция по миграции</a>, потребуется только предварительная конвертация WAL-файлов. Вы также сможете без труда вернуться с Deckhouse Prom++ на Prometheus.</p><p>Инженеры Deckhouse и сообщество помогают пользователям в Telegram-чате <a href="https://t.me/+rj_YQgUQbY1lNmIy">Prom++ User Group</a>.</p><h2>3. GMONIT</h2><p><a href="https://gmonit.ru/cio/?utm_source=PR&amp;utm_medium=globalcio&amp;utm_campaign=CIO">GMONIT </a>— российская платформа класса Observability, которая собирает и анализирует метрики, логи, трассировки и бизнес-показатели в едином интерфейсе. Она даёт ИТ-командам полный обзор цифрового контура: от сетей, серверов, баз данных и контейнеров до пользовательских действий и бизнес-метрик. GMONIT помогает перейти от «реактивного» реагирования на инциденты к проактивному управлению ИТ, сокращая время диагностики и повышая стабильность сервисов.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-10-28/a6f2924b-7955-4982-9985-c4e4fc89bd88.png" alt="" /></figure><h3>Технические возможности</h3><p>GMONIT поддерживает мониторинг сетей, серверов, виртуальных машин, баз данных, контейнеров, API, реальных пользователей в браузере или мобильном приложении и бизнес-процессов. Система строится на микросервисной архитектуре, легко масштабируется и формирует дашборды для разных ролей — от инженеров до CIO. В одном интерфейсе доступны ключевые показатели доступности, SLA, конверсии, скорость обработки заказов и другие бизнес-метрики. Платформа обеспечивает предиктивную аналитику, раннее выявление аномалий и ускоренное RCA (Root Cause Analysis — анализ первопричин): TTD (Time To Detect — время до обнаружения) — менее 10 минут, RCA — около 15 минут.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-10-28/71bdfc71-5ed6-43b1-959e-78a2dbd1f61a.png" alt="" /></figure><h3>Сценарии использования</h3><ul><li>Разработчики используют GMONIT для отладки сервисов и мониторинга CI/CD, что ускоряет релизы и сокращает время устранения ошибок.</li><li>QA-команды применяют платформу при нагрузочных тестах и фиксации ошибок в продакшене.</li><li>DevOps и SRE получают централизованный мониторинг с алертами, предиктивной аналитикой и интеграцией в пайплайны.</li><li>PM и CIO работают с визуальными панелями SLA, аптайма и бизнес-метрик, чтобы видеть реальное влияние инфраструктуры на продажи и пользовательский опыт.</li><li>Поддержка сокращает время реакции на инциденты и устраняет сбои до того, как о них сообщают пользователи.</li></ul><h3>Особенности</h3><p>GMONIT строится как масштабируемая система с единым «окном наблюдения»: метрики инфраструктуры, пользовательского опыта в браузере, приложений, API; а также вызовы во внешние сервисы и бизнес-процессы отображаются на одном дашборде. Архитектура ориентирована на работу с большими объёмами данных, а визуализация адаптирована как под инженеров, так и под управленцев. Сервис интегрируется с CI/CD, мессенджерами и сторонними системами через API. Поддерживаются как on-prem, так и облачные сценарии.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-10-28/aba0df2d-17c7-4249-9a0b-88337d507985.png" alt="" /></figure><h3>Тарифы и условия</h3><p>Информацию о тарифах можно найти на<a href="https://gmonit.ru/prices"> официальном сайте GMONIT</a>. Ограничений по числу пользователей и метрик нет. API и SDK доступны, а интеграции гибко настраиваются под нужды заказчика.</p><h3>Управление и поддержка</h3><p>Управление осуществляется через веб-интерфейс и API. Поддержка организована через чат, систему тикетов, SLA и подробную документацию.</p><h2>4. Zabbix</h2><p><a href="https://www.zabbix.com/">Zabbix</a> — это платформа мониторинга корпоративного уровня, которая обеспечивает полную наблюдаемость IT- и OT-инфраструктуры. Решение ориентировано на крупные компании и поставщиков управляемых услуг, отличается низкой совокупной стоимостью владения и предсказуемой моделью поддержки без лицензионных сборов. Zabbix создан для долгосрочного использования: он масштабируем, безопасен «по конструкции» и подходит для критически важных систем.</p><h3>Технические возможности</h3><p>Платформа поддерживает мониторинг серверов, приложений, облачных сервисов, сетевых устройств и IoT, включая многоуровневые среды. Важной функцией выступает вложенное низкоуровневое обнаружение, позволяющее автоматически создавать иерархические правила для хостов и сервисов. Zabbix предлагает мастер создания хостов, встроенную проверку форм для сокращения ошибок, расширенные сетевые карты и новый виджет карточки товара для детальной визуализации метрик. Платформа может быть развернута локально, в облаке Zabbix или в сторонних облаках (AWS, Azure, Google Cloud).</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-09-18/073c838d-dc16-44ac-a912-5e51b1b9e8d5.png" alt="" /><figcaption>Панель управления Azure</figcaption></figure><h3>Сценарии использования</h3><p>Zabbix применяют IT-отделы и DevOps-команды для централизованного мониторинга инфраструктуры и соответствия требованиям безопасности. Поставщики управляемых услуг используют его как MSP-дружественное решение с многопользовательским доступом и возможностью масштабирования под клиентов. Платформа востребована в высокозащищённых и регулируемых отраслях, где важны автономность и полный контроль над данными.</p><h3>Особенности</h3><p>Ключевые преимущества Zabbix — это открытый исходный код и отсутствие лицензионных ограничений. Архитектура платформы масштабируется «под будущее» и интегрируется с системами управления конфигурацией. Пользователи получают полное владение данными, гибкость в развертывании (on-premise, облако, гибрид) и широкие возможности кастомизации визуализации.</p><h3>Тарифы и условия</h3><p>Zabbix распространяется как решение с открытым исходным кодом и не требует лицензионных платежей. Стоимость формируется только за счет технической поддержки, которая предоставляется по фиксированным тарифам в зависимости от уровня сервиса.</p><h2>5. LibreNMS</h2><p><a href="https://www.librenms.org/">LibreNMS</a> — это система мониторинга сетевой инфраструктуры с автоматическим обнаружением устройств и сервисов. Она ориентирована прежде всего на мониторинг сетей по SNMP, но также поддерживает серверы на Windows, Linux и FreeBSD через собственные агенты. Решение работает на базе PHP и MySQL, имеет удобный веб-интерфейс, мобильные приложения и широкий набор встроенных метрик, которые не требуют ручной настройки.</p><h3>Технические возможности</h3><p>Система автоматически обнаруживает топологию сети с помощью протоколов CDP, FDP, LLDP, OSPF, BGP, SNMP и ARP. Поддерживается интеграция с NfSen, collectd, SmokePing, RANCID и Oxidized. Встроенный API позволяет управлять установкой, строить графики и выгружать данные. Реализована гибкая система оповещений с поддержкой email, IRC, Slack и других сервисов, а также встроенная биллинговая система для учета использования полосы пропускания. LibreNMS поддерживает различные методы аутентификации, включая LDAP, Radius и Active Directory, и обновляется автоматически.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-09-18/7d288b2e-8981-40f2-9ae2-3624f57392e0.png" alt="" /></figure><h3>Сценарии использования</h3><p>LibreNMS выбирают компании, которым важно быстрое развертывание мониторинга сети без сложной ручной настройки. Решение подходит интернет-провайдерам и корпоративным IT-отделам для учета трафика и выставления счетов, а также администраторам, которым нужен автоматический контроль устройств и серверов с оповещениями в удобных каналах. Благодаря мобильным приложениям и демо-версии система подходит для тестирования и удаленной работы.</p><h3>Особенности</h3><p>Ключевыми преимуществами LibreNMS выступают простота запуска и поддержка практически всех популярных сетевых устройств «из коробки». Автоматическое обнаружение и распределенный опрос упрощают масштабирование, а встроенный биллинг и интеграции делают систему полезной не только для мониторинга, но и для коммерческих задач.</p><h3>Тарифы и условия</h3><p>LibreNMS распространяется как проект с открытым исходным кодом и бесплатен для использования. Доступна онлайн-демонстрация (<a href="https://demo.librenms.org">https://demo.librenms.org</a>, логин: demo, пароль: demouser).</p>]]></content:encoded>
    </item>
    <item>
      <title>Газпромбанк запускает акселерационную программу для технологических проектов</title>
      <link>https://tproger.ru/articles/gazprombank-zapuskaet-akseleracionnuyu-programmu-dlya-tehnologicheskih-proektov</link>
      <comments>https://tproger.ru/articles/gazprombank-zapuskaet-akseleracionnuyu-programmu-dlya-tehnologicheskih-proektov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Валерия Турчак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/gazprombank-zapuskaet-akseleracionnuyu-programmu-dlya-tehnologicheskih-proektov</guid>
      <description><![CDATA[<p>Газпромбанк объявляет о запуске акселерационной программы, направленной на поддержку и развитие технологических решений для банковской отрасли и других высокотехнологичных секторов экономики.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/gazprombank-zapuskaet-akseleracionnuyu-programmu-dlya-tehnologicheskih-proektov">Газпромбанк запускает акселерационную программу для технологических проектов</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Финтех]]></category>
      <category><![CDATA[Маркетинг]]></category>
      <category><![CDATA[Наука]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Кибербезопасность]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 28 Aug 2025 08:52:33 GMT</pubDate>
      <content:encoded><![CDATA[<p>Газпромбанк объявляет о запуске акселерационной программы, направленной на поддержку и развитие технологических решений для банковской отрасли и других высокотехнологичных секторов экономики. В зависимости от стадии и технологического направления лучшие проекты могут получить потенциальную возможность запустить пилот на сумму до 10 млн рублей, привлечь финансирование от банка и инвестиционных фондов-партнеров на сумму от 30 до 500 млн рублей или сформировать продукты, готовые к работе с технологическим бизнесом и потенциальными инвесторами. Программа реализуется при участии фонда Сколково.</p><p>Участникам предоставляется доступ к менторской поддержке и отраслевой экспертизе, возможность сотрудничества с компаниями из инвестиционного портфеля Газпромбанка. В этом году программа включает три трека: Финтех, Диптех и Наука, каждый из которых ориентирован на разный уровень зрелости проектов — от стадии идеи до готового продукта с подтвержденным рыночным спросом.</p><blockquote>Запуская эту акселерационную программу, мы делаем целенаправленные инвестиции в развитие российских технологий . Наша цель — создать инфраструктуру для трансфера   перспективных разработок в реальную экономику. Программа открыта для проектов самого разного уровня — от смелой научной идеи до готового продукта с подтвержденным спросом. Мы уверены, что лучшие команды получат именно то, что нужно для роста: не просто финансирование, но также экспертизу, пилотные внедрения и прямой диалог с бизнес-заказчиками.</blockquote><p><b>Финтех трек акселератора</b> направлен на технологические решения, которые можно внедрить внутри банка. В фокусе:</p><ul><li>Маркетинг и новые каналы продаж, развитие клиентского сервиса и оптимизация клиентского пути;</li><li>Кибербезопасность, управление рисками, антифрод (внутренний, пользовательский, внешний, транзакционный);</li><li>Моделирование и скоринг, управление и работа с данными;</li><li>Удобные и безопасные транзакции;</li><li>Управление человеческим капиталом.</li></ul><p>В этот трек принимаются проекты от стадии MVP с подтвержденной работоспособностью прототипа.</p><p><b>Технологический конкурс DeepTech-проектов</b> ориентирован на привлечение инвестиций компаниями, разрабатывающими передовые решения по направлениям:</p><ul><li>Новые технологии сбережения здоровья;</li><li>Экономика данных и цифровая трансформация государства;</li><li>Космические технологии;</li><li>Новые атомные и энергетические технологии;</li><li>Новые материалы и химия;</li><li>Средства производства и автоматизации;</li><li>Технологическое обеспечение биоэкономики.</li></ul><p>К участию приглашаются проекты, которые уже прошли стадию пилота или демонстрации в реальной среде и готовы к масштабированию.</p><p><b>Трек акселератора «Наука»</b> открыт для ранних проектов, основанных на научных разработках. Участие возможно даже на стадии идеи — при наличии потенциала для технологического роста и будущей коммерциализации. В трек «Наука» приглашаются университеты - участники программы стратегического академического лидерства «Приоритет-2030» и проекта «Передовые инженерные школы».</p><p>Технологические направления проектов:</p><ul><li>Новые материалы и химия (катализаторы, малотоннажная химия, нефтехимия, моделирование и синтез катализаторов;</li><li>Новые технологии сбережения здоровья (биомедтех, агротех,бионика, медицинские изделия);</li><li>Средства производства и автоматизации (производственные технологии, станкостроение;</li><li>Новые атомные и энергетические технологии (системы накопления энергии, электрохимия);</li><li>Развитие многоспутниковой орбитальной группировки (космические технологии, плазменные двигатели, лазерная связь);</li><li>Экономика данных и цифровая трансформация (микроэлектроника, квантовые технологии).</li></ul><p>Полный список направлений и критериев отбора представлен на официальном сайте программы. Участие — бесплатное и открыто для юридических лиц и индивидуальных предпринимателей, зарегистрированных в России.</p><p>Программа реализуется в гибридном формате: ключевые активности проходят как в очном, так и онлайн форматах, а финальные презентации — на Демо-дне в очной форме.</p><p>При запуске акселератора организаторы опирались на успешный опыт предыдущего сезона и вывели его на новый уровень с расширенной программой и углубленной экспертизой. В прошлом году в программу подали заявки более 280 проектов по 14 направлениям. Эксперты Газпромбанка отобрали 20 финалистов, шесть из которых стали победителями. Все финалисты получили рекомендации по развитию продукта и провели встречи с профильными бизнес-заказчиками. По итогам акселератора два проекта уже запустили пилотные внедрения, еще пять находятся на стадии структурирования.</p><p><a href="https://rutube.ru/video/bf5255543f3fbe0bef6709052bf2d92e/?r=wd">Смотреть видео о прошлогоднем акселераторе</a></p><p>Заявки принимаются до 10 октября 2025 г. в треки «финтех» и «диптех» и до в трек «наука» до 30 сентября через официальный сайт программы.</p><p>По вопросам участия: <a><b>accelerator@twinscom.ru</b></a></p><p><a><b></b></a>Сайт: <a href="http://www.gazprombank.ru/business/accelerator-gpbtech-2025/">www.gazprombank.ru/business/accelerator-gpbtech-2025/</a></p><p>Реклама. Рекламодатель АО БАНК ГПБ, ИНН 7744001497, erid 2W5zFGRVg6c</p>]]></content:encoded>
    </item>
    <item>
      <title>6 способов автоматизировать ревью кода — подборка сервисов</title>
      <link>https://tproger.ru/articles/6-sposobov-avtomatizirovat-revyu-koda---podborka-servisov</link>
      <comments>https://tproger.ru/articles/6-sposobov-avtomatizirovat-revyu-koda---podborka-servisov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Даровская Маша]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/6-sposobov-avtomatizirovat-revyu-koda---podborka-servisov</guid>
      <description><![CDATA[<p>Устали от ручного код-ревью? Обзор инструментов, которые автоматически проверяют код, находят уязвимости и экономят время команды.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/6-sposobov-avtomatizirovat-revyu-koda---podborka-servisov">6 способов автоматизировать ревью кода — подборка сервисов</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Языки программирования]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 20 Aug 2025 12:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Код-ревью — важный этап разработки, но он часто отнимает много времени и ресурсов, особенно у ведущих специалистов. Если вы хотите ускорить процесс и повысить качество кода, не обязательно нагружать команду. Есть инструменты, которые берут часть рутины на себя.</p><h2>BeeCR: автоматический помощник для ревью в GitLab</h2><p><a href="https://beecr.ru">BeeCR</a> — сервис, который автоматически просматривает изменения в запросах на слияние (Merge Requests) в проектах GitLab и добавляет к ним комментарии. Каждый файл, который вы меняете, получает свой набор комментариев.</p><p>Сервис пригодится всем, кто пишет код на популярных языках программирования, и понимает, зачем нужно код-ревью. BeeCR работает с кодом на Python, JavaScript, Java, Kotlin, Swift, C#, C++, Ruby, PHP, Go, Bash/Shell, TypeScript и др.</p><p>Этого помощника можно добавить в команды, где процессы уже работают с живыми специалистами, или внедрить код-ревью с нуля без лишних затрат по времени и деньгам.</p><h3>Что именно проверяет?</h3><p>BeeCR — это не замена линтерам или инструментам для строгой проверки кода по конкретным гайдам. Он действует скорее как человек-ревьюер. Сервис способен обнаружить:</p><ul><li>Нарушения логики в алгоритме.</li><li>Лишний код, который случайно остался после отладки.</li><li>Ошибочность информации в логах.</li><li>Неудачное именование сущностей. Его задача — помочь найти неочевидные проблемы и дать рекомендации.</li></ul><h3>Как он встраивается в процесс разработки?</h3><p>BeeCR <b>бесшовно интегрируется с GitLab</b>. Результаты автоматического ревью отображаются прямо в привычном интерфейсе GitLab Code Review, рядом с комментариями ваших коллег.</p><p>Есть два основных способа интеграции:</p><ul><li>Через Вебхук: это самый простой и удобный вариант для большинства. Вебхук BeeCR запускается автоматически при создании или обновлении Merge Request.</li><li>Через CI/CD: если у вас уже есть развитая конфигурация GitLab CI/CD, этот вариант позволит выполнить более тонкую настройку. BeeCR предоставляет готовые конфигурации или шаблоны CI/CD-задач, которые можно использовать как есть или доработать. Для новых версий GitLab (начиная с 17) доступен CI/CD-компонент.</li></ul><h3>Можно ли настроить BeeCR под себя?</h3><p>В BeeCR можно кастомизировать разные рабочие параметры:</p><ul><li>Язык комментариев: по умолчанию комментарии пишутся на английском, но вы можете указать другой язык, например, русский.</li><li>Фильтрация файлов: управляйте списком файлов для проверки (или исключения) с помощью регулярных выражений.</li><li>Языковая модель: в версии On-Premises можно выбрать языковую модель (например, OpenAI или Ollama) и настроить её параметры.</li><li>Дополнительные инструкции: можно задать промты, чтобы получить более специфичные комментарии, через конфигурационный файл или комментарий в Merge Request.</li><li>Триггер и путь к конфигу: вы можете переопределить ключевое выражение-триггер (по умолчанию /beecr) и путь к конфигурационному файлу .beecr.yml. Настройки задаются через HTTP-заголовки вебхука, параметры запроса в URL, конфигурационный файл .beecr.yml в корне репозитория, переменные окружения сервера API (для On-Premises) или параметры задачи CI/CD.</li></ul><h3>Как попробовать и сколько это стоит?</h3><p>BeeCR предлагает пробный период на 1 месяц без ограничений. Стоимость рассчитывается по количеству активных участников в запросах на слияние в GitLab. К таким участникам относятся разработчики, ревьюверы, участники дискуссий и ответственные за слияние ветки. Сервис доступен в облачной версии (SaaS) или для локальной установки (On-Premises).</p><p>В версии On-Premises можно использовать как локально развернутые, так и внешние модели ИИ (например, ChatGPT), при этом оплата внешних моделей ложится на клиента. BeeCR включен в реестр Российского программного обеспечения (реестровая запись №25382 от 12.12.2024).</p><h2>Reshift — автоматическая проверка безопасности JS-кода</h2><p><a href="https://github.com/Reshift-Security/npm_plugin">Reshift</a> предлагает легковесный JavaScript-плагин для npm, призванный выявлять уязвимости ещё на этапе разработки. Он выполняет быстрое сканирование изменений и даёт подсказки по исправлению, одновременно обучая и поддерживая разработчика через понятные рекомендации.</p><p>Поддерживаемые языки: JavaScript.</p><h2>Что именно проверяет?</h2><p>В функции плагина входят:</p><ul><li>Набор проверок безопасности, разработанных экспертами в области безопасности;</li><li>Подробные, понятные описания обнаруженных проблем (DevSec Coach);</li><li>Рекомендации по исправлению (remediation snippets);</li><li>Ссылки на дополнительные ресурсы по уязвимостям.</li></ul><h2>Как встраивается?</h2><p>Сейчас плагин доступен только ограниченной группе участников закрытого бета-тестирования. Чтобы подключиться, нужно записаться на waitlist через сайт Reshift.</p><h2>Collaborator — расширенное ревью с фокусом на командную работу</h2><p><a href="https://smartbear.com/product/collaborator/">Collaborator</a> от SmartBear — это мощный инструмент для организации командного ревью кода, документации и тестов. Он не ограничивается автоматической проверкой — ключевая особенность сервиса в том, что он объединяет разработчиков, тестировщиков, аналитиков и технических писателей в едином процессе. Здесь ревью — это не только поиск ошибок, но и улучшение коммуникации внутри команды.</p><p>Практически все популярные языки программирования, включая Java, Python, JavaScript, C#, C++, PHP, Go, Swift, Kotlin и др. Также поддерживается ревью не только исходного кода, но и документации (Markdown, reStructuredText, HTML, PDF, Word) и тестовых сценариев.</p><h2>Что именно проверяет?</h2><p>Collaborator сочетает автоматическую проверку и ручное ревью, в его функции входят:</p><ul><li>Ревью исходного кода и документов (Word, PowerPoint, Visio, PDF, изображения, URL и др.)</li><li>Совместное обсуждение изменений, пометки дефектов и контроль версий</li><li>Возможность создания шаблонов ревью, чек-листов и пользовательских рабочих процессов</li><li>Отчёты для анализа процессов, отслеживания дефектов и аудита</li></ul><h2>Как встраивается?</h2><ul><li>Поддерживаются более 11 систем контроля версий: Git, Subversion, Perforce, TFS, CVS, Mercurial, ClearCase, AccuRev, Rational Team Concert и др.</li><li>Также интеграция со средами разработки (Eclipse, Visual Studio), Jira, Web UI и API.</li></ul><h2>Можно ли настроить под себя?</h2><ul><li>Создание чек-листов для обязательных пунктов ревью (например, безопасность, оптимизация, тестирование).</li><li>Настройка уровней серьёзности комментариев (информация, предупреждение, ошибка).</li><li>Возможность использования шаблонов ревью и шаблонов отчётов для стандартизации процесса.</li><li>Поддержка метрик — можно отслеживать, сколько дефектов найдено, сколько времени уходит на ревью, кто из участников наиболее активен.</li></ul><h2>Как попробовать и сколько стоит?</h2><p><b>Community</b> — бесплатная для небольших команд (до ~10 пользователей), с базовым набором функций.</p><p><b>Team</b> — платная, для средних команд (до ~25 пользователей), с расширенными возможностями ревью. $755 в год за пакет на 5 пользователей (до 25 человек).</p><p><b>Enterprise</b> — полная версия для крупных организаций; включает мощные настройки, интеграции и поддержку Simulink моделей. Около $1349 в год за одну concurrent-лицензию, включая поддержку Simulink и расширенные возможности.</p><h2>Codestriker — лёгкий инструмент для асинхронного ревью</h2><p><a href="https://codestriker.sourceforge.net">Codestriker</a> —  это веб-приложение с открытым исходным кодом (написано на Perl под GPL), предназначенное для асинхронного код-ревью. Оно поддерживает работу с диффами из систем контроля версий и предоставляет удобный интерфейс для коллективного ревью.</p><p>Работает с любыми языками программирования, поскольку анализирует диффы и текст. Поддерживает также документальные ревью.</p><h2>Что именно проверяет?</h2><p>Сам по себе Codestriker не выполняет <b>глубокий автоматический</b> анализ кода.</p><p>Для статического анализа, проверки стиля или поиска уязвимостей его можно дополнить линтерами, CI-скриптами или внешними утилитами.</p><p>Основная функция — хранить диффы и обсуждения, предоставляя удобный интерфейс для комментариев и фиксации договорённостей.</p><h2>Как встраивается в процесс разработки?</h2><ul><li>Работает через веб-интерфейс, не требует сложной установки.</li><li>Поддерживает загрузку диффов и изменений напрямую или через интеграцию с системами контроля версий.</li><li>Сохраняет историю всех ревью для удобного поиска и аудита.</li><li>Поддерживает экспорт результатов и комментариев в HTML или текстовые файлы — полезно для отчётности и архивирования.</li><li>Интегрируется с системами контроля версий: CVS, Subversion, ClearCase, Perforce, Visual SourceSafe.</li><li>Паспортная интеграция с баг-трекерами (Bugzilla, Flyspray) и системой LXR для быстрого просмотра кода.</li></ul><h2>Можно ли настроить под себя?</h2><p>Можно дорабатывать под свои процессы, так как это open-source.</p><ul><li>Поскольку Codestriker — это open-source, его можно модифицировать под внутренние процессы: добавить плагины, интеграцию с баг-трекерами, отчёты в специфическом формате.</li><li>Можно настраивать шаблоны комментариев, порядок отображения изменений, форматы экспорта.</li><li>Код доступен для свободной доработки, что делает сервис особенно привлекательным для команд с собственными требованиями.</li><li>Требует наличие сервера и СУБД — поддерживаются MySQL, PostgreSQL, Oracle, SQL Server и любая база через DBI.</li><li>Разворачивается на сервере: конфигурация веб-сервера (Apache/IIS), базы данных, установка Perl-модулей.</li></ul><h2>Как попробовать и сколько стоит?</h2><ul><li><b>Полностью бесплатен</b> — распространяется под открытой лицензией.</li><li>Можно развернуть на любом сервере или даже локально.</li><li>Подходит как временное решение для небольших команд, так и как база для создания собственного инструмента ревью.</li></ul><h2>Gerrit — мощный инструмент для распределённых команд</h2><p><a href="https://www.gerritcodereview.com/">Gerrit</a> — это мощная open-source платформа для ревью кода, тесно интегрированная с Git. Она используется во многих крупных компаниях и open-source проектах, где требуется строгий контроль качества кода и формальный процесс утверждения изменений.</p><p>Поддерживаемые языки: любые Gerrit работает на уровне изменений в Git, а не конкретного языка. Можно ревьюить и код, и документацию, и конфигурационные файлы.</p><p>Проект начал развиваться как форк от системы Rietveld и был создан Шоном Пирсом (одним из авторов Git) для внутренней разработки Android в Google .</p><p>В версии 2.x проект был переписан с Python на Java (Java EE), с использованием SQL для метаданных. Начиная с версии 3.x, Gerrit перешёл на NoteDb, где все метаданные хранятся непосредственно в Git-репозитории.</p><h2>Что именно проверяет?</h2><p>Сам по себе Gerrit не выполняет статический анализ, но легко интегрируется с линтерами, CI/CD-пайплайнами и системами тестирования. Основная задача — управление процессом ревью: комментарии к конкретным строкам, обсуждения, обязательное одобрение перед слиянием. Поддерживает настройку правил мёрджа (например, «два апрува от сеньоров» или «все тесты должны пройти»).</p><p>Пользователи создают review, отправляя коммит на refs/for/branch, что отличает изменения, требующие ревью, от прямых коммитов. Ревьюеры могут голосовать, модель оценки, например, используется шкала от -2 до +2 для Code-Review и Verified чеков. Доступно изменение изменений в рамках одного change (patch set), с возможностью обсуждения и улучшения кода до принятия.</p><h2>Как встраивается?</h2><ul><li>Работает как веб-приложение поверх Git. Поддержка SSH и HTTPS для работы с Git-клиентами, возможность размещения и управления множеством репозиториев с продвинутым контролем доступа и репликацией (например, геораспределённые зеркала)</li><li>Предоставляет визуальный интерфейс для сравнения изменений и обсуждения.</li><li>Глубокая интеграция с Jenkins, GitHub Actions, GitLab CI и другими CI/CD-системами.</li><li>Поддерживает fine-grained permissions — можно задать права на просмотр, комментирование и утверждение кода на уровне проекта, папки или файла.</li><li>Расширяемый через серверные плагины, доступные в официальном репозитории.</li></ul><h2>Можно ли настроить под себя?</h2><p>Доступны плагины, собственные скрипты, тонкая настройка прав доступа:</p><ul><li>Плагины для интеграции с трекерами задач (Jira, Redmine и др.).</li><li>Скрипты для автоматизации ревью и проверки коммитов.</li><li>Возможность настроить workflow под конкретный проект: обязательные шаги, правила коммит-месседжей, формат патчей.</li></ul><h2>Как попробовать и сколько стоит?</h2><ul><li><b>Полностью бесплатен</b>, распространяется под лицензией Apache 2.0.</li><li>Можно развернуть на своих серверах или использовать облачные инсталляции.</li><li>Отлично подходит для крупных команд с формализованным процессом разработки.</li><li>Gerrit — полностью open-source. Обновления можно скачивать в виде .war или через Maven Central, подписанные ключом автора.</li><li>Сообщество активно поддерживает релизы, документацию и обсуждения через форумы, Discord и пользовательские мероприятия.</li></ul><h2>Crucible — код-ревью с аналитикой и интеграцией в Atlassian</h2><p><a href="https://www.atlassian.com/software/crucible">Crucible</a> от Atlassian — это инструмент для код-ревью, ориентированный на большие компании, которым нужны интеграции с экосистемой Atlassian (Jira, Bitbucket, Confluence) и детальная аналитика по процессу ревью.</p><p>Поддерживаемые языки: любые, так как Crucible анализирует текстовые изменения. Есть готовые плагины для популярных языков.</p><h2>Что именно проверяет?</h2><ul><li>Логические ошибки;</li><li>Историю коммитов с возможностью поиска;</li><li>Потенциальные баги и нарушения стиля кода (с помощью подключаемых линтеров);</li><li>Несоответствия между кодом и документацией;</li><li>Общие архитектурные недочёты (в рамках командного обсуждения).</li></ul><p>Доступны Review и обсуждения — есть возможность комментировать строки кода, запускать threaded-дискуссии, упоминания и встроенные обсуждения. Ленты событий показывают последние комментарии, обновления, активность команды.</p><h2>Как встраивается?</h2><ul><li>Поддерживает Git, SVN, Mercurial, Perforce и CVS.</li><li>Интегрируется с Jira — можно привязывать задачи к конкретным ревью.</li><li>Есть возможность планировать ревью, назначать ответственных, отслеживать прогресс и статистику по команде.</li><li>Поддерживает расширение через REST API, можно создавать надстройки через плагины.</li></ul><h2>Можно ли настроить под себя?</h2><p>Это кроссплатформенное (Windows, Linux, macOS)  Java-приложение — требует x86‑64 или аналогичный. Подключаются официально поддерживаемые СУБД: HSQLDB (для тестирования), MySQL, PostgreSQL, Oracle, SQL Server.</p><ul><li>Настраиваемые шаблоны ревью.</li><li>Чек-листы для проверки типовых требований.</li><li>Автоматические уведомления и напоминания для участников.</li><li>Гибкая настройка прав доступа.</li></ul><h2>Как попробовать и сколько это стоит?</h2><ul><li>Платная лицензия: от $10 для 5 пользователей, далее цена зависит от команды.</li><li>Есть 30-дневный бесплатный пробный период.</li><li>Оптимально для компаний, уже использующих Atlassian Jira и Bitbucket.</li></ul>]]></content:encoded>
    </item>
  </channel>
</rss>