Меня зовут Артур Валиев (Поможет в трудоустройстве). Я Rust-разработчик. Который пишет про врачей, любовь, смерть и кодеки.

Некоторое время назад я отошёл от EvertyDesk, но недавно команда снова позвала меня помочь с несколькими техническими проблемами. (Я обожаю когда меня зовут) разумеется не за просто так.

И вот здесь начинается вещь, которая мне кажется куда "интереснее" самого факта возвращения.

Когда продукт уже большой и давно живёт своей жизнью, задача редко приходит к тебе в чистом виде.

Никто не говорит:

В zstd::decode_all на каждом тайле создаётся новый контекст, замени его на reusable decoder и получишь шестикратное ускорение.

Обычно говорят примерно так:

Что-то тормозит.

И дальше твоя настоящая работа — не написать код.

Твоя работа — найти задачу.

Тут я специально расскажу что это, ведь я теперь писарь, ZSTD это не просто набор букв, это Zstandard - это современный высокопроизводительный алгоритм и утилита для сжатия данных без потерь, (Не лучшая) но имеющая чертовский баланс. Например альтернативы LZ4, Gzip, Brotli, Snappy, LZMA, xz, bzip2, Zlib но тут можно чуть дальше пойти и рассказать про H.264, H.265, AV1, WebP, WebM, Tight, ZRLE, RDP-6.0, MJPEG. В целом, это когда такие щенки как я берут и используют годы работ других чертовски гениальных ребят для "Adaptive Compression / Adaptive Coding"

Что-ж, я V. Добро пожаловать.

EVRTГусь.
EVRTГусь.

Читаем, EvertyDesk боевик.

Исходная проблема: decode иногда занимал десятки миллисекунд

Стоп СТОП! Что такое “Decode” - Это когда тебе приходит - пойдем на ужин и тебе надо это переварить до ужина а Encode это когда ты этот ужин обратно в рецепт превращаешь.

В живом hard-test EvertyDesk были видны просадки:

decode_ms ≈ до 51 ms

Для удалённого рабочего стола это уже слишком много.

Особенно если речь идёт не о полной end-to-end latency, а только о декодировании изображения.

Поэтому первым шагом я вынес EVRTCK из общего pipeline и измерил отдельно.

Плотный keyframe:

2560×1440

Результат:

Full keyframe decode: 89.9 ms

Это уже было не ощущение.

Не:

Кажется, сеть тупит.

Не:

Может быть GPU.

Не:

Возможно, тестовая машина.

А конкретные:

89.9 миллисекунды внутри decode path.

С этого момента можно было начинать разбирать код.

Десятки мили гусей
Десятки мили гусей

Проблема оказалась не в zstd

EVRTCK разбивает изображение на большое количество тайлов.

Для 2560×1440 в одном кадре обрабатывается примерно несколько тысяч отдельных блоков.

Encode и decode каждого блока использовали одноразовый API zstd:

zstd::encode_all(...)
zstd::decode_all(...)

На первый взгляд ничего подозрительного.

Но эти функции рассчитаны на самостоятельную операцию сжатия или распаковки.

При каждом вызове создаётся новый контекст.

То есть внутри одного кадра происходило примерно это:

create decoder
decode tile
destroy decoder

create decoder
decode tile
destroy decoder

create decoder
decode tile
destroy decoder

...

Тысячи раз.

Сам алгоритм zstd мог работать быстро.

Но вокруг него постоянно создавалось и уничтожалось состояние.

Именно поэтому я люблю сначала измерять систему целиком, а потом спускаться вниз.

Потому что иначе можно неделю оптимизировать entropy coding, SIMD или transport, хотя основное время уходит на создание одного и того же объекта несколько тысяч раз.

Привет, я Артур а Вы кто?

EvertyDesk Reusable ZSTD Context
EvertyDesk Reusable ZSTD Context
Уровни от Гуся ZSTD От 1 До 22
Уровни от Гуся ZSTD От 1 До 22

WOW, ZSTD это типа RAR или ZIP мы что-то сжимаем на входе и на выходе, тут ZSTD просто баланс, хотите лучше на входе зажать значит напрягитесь сильнее.

Немного про ZSTD, потому что я теперь писарь и обязан объяснять буквы (Заголовок)

Прежде чем продолжить, придётся рассказать, что такое zstd. Я теперь, видимо, не только разработчик, но и литератор, поэтому нельзя просто написать zstd и сделать вид, что все родились с man-страницей в руках.

Zstandard, или zstd, — алгоритм сжатия без потерь, разработанный Facebook, ныне Meta. Он не является священным Граалем компрессии, не лечит простуду, не возвращает бывших и не делает плохую архитектуру хорошей. Но у него чертовски удачный баланс скорости, коэффициента сжатия и практичности. Рядом живут LZ4, gzip, Brotli, Snappy, LZMA, xz, bzip2, zlib и ещё огромное кладбище прекрасных инженерных идей, каждая из которых в правильном месте великолепна, а в неправильном способна превратить ваш realtime pipeline в ZIP-архив 2007 года.

А если начать говорить именно про удалённый рабочий стол, рядом внезапно появляются H.264, H.265, AV1, VP9, WebP, WebM, MJPEG, Tight, ZRLE, всякие RDP-кодеки и ещё десятки подходов. Правда, сравнивать их в одну строку технически нечестно: часть из них — универсальные lossless-компрессоры, часть — видеоформаты, часть — encoding schemes для remote display. Но пользователю на другом конце провода абсолютно всё равно, как мы классифицируем это на конференции. У него мышь либо двигается сейчас, либо через сто миллисекунд. И вот тут начинается то, что маркетологи красиво называют Adaptive Compression / Adaptive Coding, а такие щенки, как я, называют значительно проще: мы берём десятилетия работы людей умнее нас и пытаемся не использовать их самым идиотским способом.

51 миллисекунда, которая оказалась только разминкой

В живом hard-test EvertyDesk периодически появлялась довольно неприятная цифра: decode_ms доходил примерно до 51 миллисекунды. Для программы удалённого доступа это уже не «ну, наверное, можно потом посмотреть». Пятьдесят миллисекунд только на декодирование — это огромный кусок бюджета задержки. И это ещё до того, как изображение действительно оказалось перед глазами пользователя. До этого была сеть, после этого будет композиция, очередь GPU, refresh дисплея, ввод и вся остальная вечеринка.

Первое, что здесь очень хочется сделать, — начать обвинять сеть. Сеть вообще чрезвычайно удобный подозреваемый. У неё нет адвоката. Можно сказать «джиттер», «NAT», «TCP Head-of-Line», «провайдер», «Wi-Fi», сделать серьёзное лицо и на несколько часов отложить необходимость смотреть на собственный код.

Но decode_ms — зараза конкретная. Поэтому я вынес EVRTCK из общего pipeline и начал мерить отдельно. Берём плотный keyframe 2560×1440. Не красивую сцену, не идеально пустой рабочий стол, а нормальную тяжёлую работу для декодера.

Получаем:

Full keyframe decode: 89.9 ms

Восемьдесят девять и девять десятых миллисекунды.

Это даже удобно. После такой цифры заканчивается эзотерика. Не «кажется медленно», не «может, GPU», не «возможно, Windows опять занимается Windows». Есть конкретная операция. Есть почти девяносто миллисекунд. Где-то внутри неё лежит труп.

Можно начинать вскрытие.

ZSTD не тормозил. Мы просто очень старательно мешали ему работать

EVRTCK разбивает экран на тайлы 32×32. Полный кадр 2560×1440 — это несколько тысяч отдельных блоков. И каждый такой блок мог отдельно пройти через сжатие или распаковку. Это нормальная архитектура для нашего случая: можно эффективно работать с изменившимися областями и не гонять весь экран без необходимости.

Ненормальным оказался другой момент.

Для каждого тайла использовался удобный одноразовый API:

zstd::encode_all(...)
zstd::decode_all(...)

Это тот случай, когда API настолько приятный, что хочется обнять автора. Передал данные, получил результат, пошёл дальше. Только за удобство иногда платит не программист, а процессор.

Каждый такой вызов создавал новый контекст zstd, выполнял одну маленькую операцию, после чего контекст выбрасывался. Следующий тайл — новый контекст. Ещё тайл — ещё один. И так несколько тысяч раз внутри одного кадра.

Представьте автомеханика, которому надо закрутить 3600 гаек. После каждой гайки он выбрасывает ключ, идёт в магазин, покупает новый, возвращается, закручивает следующую гайку, снова выбрасывает ключ. В конце рабочего дня он пишет отчёт: «Резьбовые соединения демонстрируют неудовлетворительную производительность. Возможно, фундаментальное ограничение гаек».

Вот примерно этим занимался EVRTCK.

Сам zstd мог быть быстрым. Полезная работа могла занимать сравнительно немного времени. Но мы окружили её таким количеством организационной деятельности, что начали измерять в основном не компрессию, а собственную способность пересоздавать компрессор.

И вот почему профилирование полезнее гордости. Без цифр можно было неделю обсуждать entropy coding, SIMD, allocator, GPU offload и написать такой прекрасный новый кодек, что старый баг от смеха умер бы сам.

Фикс №1: перестать выбрасывать молоток после каждого гвоздя

Исправление было почти оскорбительно простым. Вместо одноразовых вызовов появились thread-local объекты:

zstd::bulk::Compressor
zstd::bulk::Decompressor

Создаём контекст один раз на поток. Используем его снова. Потом ещё раз. И ещё. То есть применяем к компьютерной программе революционную технологию, известную человечеству со времён каменного топора: если инструмент ещё нужен, можно его не выбрасывать.

Формат EVRTCK не поменялся. Алгоритм не поменялся. Данные на выходе не стали волшебными. Никакой новой научной статьи не появилось. Никто не переписал zstd на ассемблере во время полнолуния.

Просто вот результат decode плотного keyframe 2560×1440:

89.9 ms → 14.2 ms

Ускорение — 6,3 раза.

На 1920×1080 encode, где было около двух тысяч тайлов:

65.8 ms → 4.4 ms

Примерно 15 раз.

А размер результата — тот же.

Это тот тип оптимизации, который мне особенно нравится, потому что он унижает разработчика максимально экологично. Ты приходишь в код в поисках сложной проблемы, готовый сражаться с алгоритмами, а код показывает тебе пальцем на мусорное ведро и спрашивает: «А может, ты просто перестанешь туда выбрасывать рабочий инструмент каждые двадцать микросекунд?»

Вот полный срез первого фикса:

Сценарий

До

После

Ускорение

Decode, плотный keyframe 2560×1440

89.9 мс

14.2 мс

6.3×

Decode, mostly-solid keyframe 2560×1440

18.6 мс

8.0 мс

2.3×

Decode, delta на 1000 тайлов

13.4 мс

2.9 мс

4.6×

Encode, keyframe 1920×1080, ~2000 тайлов

65.8 мс

4.4 мс

≈15×

Здесь можно включить 16BL — More Than Music, посмотреть на таблицу и несколько минут подумать о природе человеческой самоуверенности.

Потому что дальше стало хуже.

Старый benchmark не соврал. Это мы задали ему тупой вопрос

Раньше мы уже пробовали поднять уровень zstd с первого до шестого. И получили вполне убедительный результат: более высокая компрессия экономит трафик, но становится почти в десять раз медленнее. Решение очевидное — оставить level 1. Remote desktop должен реагировать быстро, а не философски размышлять над каждым байтом.

Нормальное инженерное решение.

Подкреплённое цифрами.

То есть именно тот вид ошибки, которому особенно приятно доверять.

После исправления reusable context я посмотрел на старый benchmark и понял неприятную вещь: повторить его теперь придётся обязательно. Потому что раньше мы измеряли не только стоимость compression level. Мы измеряли стоимость compression level плюс нашу патологическую любовь пересоздавать контекст для каждого тайла.

И вот это прекрасный момент в работе с производительностью. Benchmark может быть абсолютно честным. Таймер работает правильно. Числа записаны без ошибки. CSV настоящий. График красивый. Никто ничего не подделывал.

И вывод всё равно мусор.

Потому что вы измерили не ту систему.

Повторяем эксперимент после исправления.

Переходим с:

zstd level 1

на:

zstd level 6

Размер:

203 KB → 123 KB

Экономия — примерно 39%.

А цена?

Примерно одна миллисекунда.

Не десятикратное замедление.

Одна миллисекунда.

В этот момент разработчик кодека должен отнестись к ситуации спокойно и профессионально. То есть закрыть глаза, глубоко задуматься и мысленно пожелать предыдущему benchmark всего самого доброго.

Потому что мы буквально приняли архитектурный баг за фундаментальное свойство алгоритма.

И вот это уже интереснее самой оптимизации.

Ошибка «я не измерил» простительна.

Ошибка «я измерил неправильно» неприятна.

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

Цифры не лгут.

Зато мы великолепно умеем задавать цифрам вопросы так, чтобы потом соврать самим себе.

Фикс №2: а давайте теперь честно спросим zstd, чего он стоит

После повторных измерений EVRTCK перешёл с level 1 на level 6. Но после истории с прошлым benchmark просто выбрать шестой уровень и объявить победу было бы уже каким-то особым видом наглости.

Поэтому мы прогнали уровни сжатия дальше.

От 1 до 22.

На трёх типах содержимого.

В общей сложности 42 замера.

И здесь цель была не «найти минимальный файл». Если хочется минимальный файл любой ценой, можно просто дать компрессору вечность и уйти пить чай. У нас интерактивная система. Пользователь в это время двигает мышь. Он вообще не заинтересован в том, насколько эстетично мы ужали предыдущий кадр.

Правильный вопрос звучал иначе:

Где заканчивается разумная экономия трафика и начинается сжигание процессора ради удовольствия смотреть на меньшее число байтов?

Вот это уже задача.

И график ответил довольно честно.

От level 1 до level 22: сначала инженерия, потом азартные игры

Первые уровни — примерно 1–3 — ведут себя спокойно. Какие-то изменения есть, но мир не переворачивается. Потом на переходе к уровню 4 появляется заметный перелом: размер начинает уменьшаться ощутимее, а стоимость по CPU всё ещё остаётся разумной.

Дальше, примерно до десятого уровня, существует область, где ещё можно нормально торговаться: хочешь чуть больше компрессии — плати чуть больше времени. Никакой магии, просто экономика.

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

А потом zstd начинает предлагать вам финансовые продукты с повышенным риском.

На одном из плотных keyframe-тестов:

level 6  ≈ 22 ms
level 22 ≈ 973 ms

973 миллисекунды.

Почти секунда.

Для одного кадра.

В remote desktop.

На этом месте пользователь уже успел нажать кнопку, решить, что программа зависла, нажать её ещё четыре раза и начать писать в поддержку.

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

Процессор думает.

Алгоритм старается.

Температура растёт.

Байты стоят на весах и отказываются худеть.

И тогда возникает философская проблема, знакомая не только по кодекам: если параметр можно увеличить до 22, это совершенно не означает, что человек обязан это сделать.

Мы вообще удивительный вид. Нам дают slider — мы хотим выкрутить его вправо. Нам дают 32 потока — мы создаём 32 потока. Нам дают retry — мы ретраим до тепловой смерти Вселенной. Нам дают уровень компрессии 22 — и где-то обязательно находится человек, который спрашивает: «А почему не используем максимальный?»

Потому что максимум и оптимум — разные слова не случайно.

Золотой грааль оказался не золотым и даже не граалем

В итоге level 6 остался не потому, что шестёрка обладает мистическими свойствами. Просто в наших измерениях это хорошая точка до того, как стоимость начинает расти быстрее пользы.

И, пожалуй, именно это я называю оптимизацией сейчас.

Не найти максимальное значение.

Не выиграть benchmark любой ценой.

Не сделать график, который эффектно смотрится в Telegram.

А найти место, где дальнейшие улучшения становятся экономически бессмысленными.

Remote desktop особенно хорошо лечит от любви к максимальным цифрам. Здесь нельзя сказать пользователю:

Да, курсор приехал на 700 миллисекунд позже, зато мы передали на 11 килобайт меньше.

Пользователь не оценит.

Он вообще неблагодарный человек. Ему почему-то хочется, чтобы мышь двигалась тогда, когда он двигает мышь.

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

После микробенчей мы снова запустили живой hard-test. Это принципиально важно, потому что synthetic benchmark — это прекрасный инструмент, но у него есть один недостаток: synthetic benchmark не пользуется EvertyDesk.

Пользуется человек.

До исправлений в живой системе decode_ms мог подниматься примерно до:

51 ms

После:

3–11 ms

При этом по экрану реально ездили мышью, печатали, скроллили и создавали обычные изменения рабочего стола.

Вот после этого я уже готов считать работу реальной оптимизацией.

До живого теста это просто интересная гипотеза с красивой таблицей.

После живого теста — изменение продукта.

И тут можно было бы спокойно закончить работу, закрыть ноутбук и написать статью о том, как Артур героически победил zstd. Но zstd, как выяснилось, вообще не был противником. Он просто молча терпел наши издевательства.

А EvertyDesk тем временем приготовил следующую серию.

Пользователь сказал «нет». Клиент услышал «перезвони через пять секунд»

Параллельно была проблема с входящими подключениями. Хост получает запрос, пользователь нажимает «Отклонить», и вроде бы на этом человеческое общение должно закончиться.

Но viewer имел собственное мнение о границах согласия.

Внутри одной попытки могло уйти ещё несколько LoginRequest. А после этого запускался автоматический reconnect с интервалами от пяти до сорока секунд.

То есть пользователь сказал:

Нет.

А клиент ответил:

Я понял. Вернусь позже.

Это уже не fault tolerance. Это бывший, который «просто хотел уточнить».

Причина была вполне техническая и потому особенно смешная: код воспринимал отказ примерно так же, как временную сетевую проблему. Соединение не состоялось? Значит, можно попробовать ещё раз.

Но в распределённой системе важно не только то, что произошло, а почему.

Timeout означает:

Мы не знаем, что произошло.

Rejected означает:

Мы прекрасно знаем, что произошло. Нас послали.

Это совершенно разные состояния, даже если конечный результат у обоих один — сессия не установлена.

Мы сделали отказ терминальным для автоматической попытки. Никакого бесконечного reconnect после явного Rejected. При этом ручной retry сохранился: пользователь может нажать Connect снова, если действительно хочет отправить новый запрос.

Программа наконец научилась отличать настойчивость от отсутствия воспитания.

А потом оказалось, что кнопка «Отклонить» вообще не появлялась

Вот здесь история приобрела любимый мной оттенок абсурда.

Мы пошли проверять исправление в реальном production flow.

Запускаем.

Отправляем запрос.

Ждём approval popup.

Ничего.

Ждём ещё.

Ничего.

Через 45 секунд timeout.

И тут выясняется: в стандартном режиме запуска approval dialog вообще не показывался.

То есть несколько минут назад мы очень серьёзно исправляли поведение программы после нажатия кнопки, которую пользователь в нормальном сценарии вообще не видел.

Вот за такие моменты я и люблю разработку.

Она регулярно напоминает, что чувство собственного интеллектуального превосходства — ресурс возобновляемый, но крайне нестабильный.

Причина оказалась трёхэтажной. Реальный хостинг уже жил в detached --host-agent. Старый механизм popup зависел от desktop-gui, которую новый desktop-next намеренно не подключал, потому что та тянула старый egui поверх нового iced. А даже если бы мы вернули эту feature, механизм пытался запустить <binary> --approval-prompt <id>, но актуальный launcher этот аргумент вообще не обрабатывал.

Все компоненты существовали.

Все компилировались.

Каждый по отдельности выглядел так, будто выполняет важную работу.

Вместе они образовывали великолепный распределённый театр, в котором актёры вышли на разные сцены.

И это, пожалуй, одна из главных особенностей старого production-кода: он редко ломается красивым взрывом. Чаще он тихо расходится сам с собой во времени.

Когда тебя зовут обратно, иногда зовут не за кодом

Вот здесь можно вернуться к началу статьи.

Почему вообще позвали меня?

Вероятно, потому что я очень красивый.

Но есть и менее фантастическая версия.

Когда ты долго работал над проектом, у тебя в голове остаётся его история. Не только текущие файлы, а причины, почему они появились. Ты помнишь, что когда-то был egui, потом появился iced. Что host жил одним способом, потом другим. Что один бинарник раньше был главным, а теперь фактически музейный экспонат. Почему появился конкретный флаг. Почему какой-то benchmark когда-то считался доказательством.

Git замечательно хранит код.

Он значительно хуже хранит контекст человеческих решений.

README обычно тоже не содержит раздела:

Здесь лежит старый клиент. Мы его вроде больше не используем, но удалить страшно, потому что никто не помнит, кто последний его запускал.

Поэтому бывшего разработчика иногда зовут не потому, что без него никто не способен написать if.

Его зовут как археолога.

Показать ему кусок стены и спросить:

Это несущая?

А он смотрит и отвечает:

Нет. Это мы в 2024 году временно поставили. Почему она до сих пор здесь?

И именно такие ответы иногда экономят больше времени, чем любое знание синтаксиса Rust.

Хотя, повторюсь, предпочитаю версию про красоту.

Правильный фикс в неправильной программе — тоже неправильный фикс

В ходе этой сессии я успел сделать ещё одну абсолютно классическую вещь: сначала исправил retry в src/main.rs.

Код правильный.

Решение правильное.

Логика правильная.

Проблема одна.

Это старый evertydesk-lite.

Настоящий release работает через desktop-next.

Я исправил баг в программе, которой никто не пользуется.

Это, кстати, невероятно чистая форма программирования. Пользователей нет — багов тоже больше нет.

Пришлось откатывать и идти в реальные viewer.rs, launcher.rs, transport.rs, protocol.rs.

С тех пор у меня появился ещё один очень серьёзный технический вопрос, который стоит задавать до профилирования, рефакторинга и проектирования:

Этот код вообще кто-нибудь запускает?

Потому что 100% ускорение неиспользуемого бинарника по-прежнему даёт пользователю примерно 0%.

Красный тест, ставший членом семьи

Под конец оставался тест quality_profiles_change_transport_display_settings.

Он падал несколько раз.

Но это был «известный старый failure».

У каждого взрослого проекта есть такие тесты. Сначала красный тест раздражает. Потом команда привыкает. Ещё через месяц он становится частью интерфейса терминала. Если однажды исчезнет, кто-нибудь обязательно спросит, куда он делся.

Мы всё-таки открыли его.

И оказалось, что production-код уже давно намеренно поднял min_fps с 15 до 30. Причина даже была записана: при пятнадцати кадрах картинка воспринималась как замёрзшая.

Код изменили.

Поведение изменили.

А тест продолжал ждать прошлое.

То есть тест успешно защищал систему от правильного поведения.

После обновления assert получили:

39 / 39 passed

И это важно не из-за эстетики зелёной строки.

Когда suite постоянно имеет «те самые два-три известных падения», новое настоящее падение становится значительно легче проигнорировать.

Красный цвет перестаёт означать тревогу.

Он начинает означать интерьер.

Поэтому stale test — это не просто мусор. Это маленькая диверсия против будущего разработчика.

418 тестов и право немного успокоиться

На уровне основной библиотеки мы на каждом этапе гоняли:

cargo test --lib --release

Получали:

418 passed
2 known pre-existing failures

А после уборки desktop-next уже дал полностью зелёные 39/39.

Особенно важно было проверить reusable context. Потому что переиспользуемое состояние — это всегда территория, где можно очень быстро перейти от «ускорили в 15 раз» к «ускорили генерацию повреждённых данных в 15 раз».

Но output остался тем же, тесты прошли, live-test подтвердил выигрыш.

После этого можно было считать работу законченной.

Почти.

Вишенка: а тот ли EXE мы вообще отправили?

В конце меня попросили проверить релизный launcher.exe.

Точно ли там сегодняшние изменения?

Можно посмотреть дату файла.

Можно посмотреть размер.

Можно поверить build pipeline.

Можно перекреститься.

Но мы сделали проще: поискали характерные строки прямо внутри бинарника.

approval-prompt
Login refused: Rejected

Обе строки присутствовали.

Элегантно? Не особенно.

Работает для поставленного вопроса? Да.

Разработчики иногда очень любят сооружать систему доказательства существования табуретки из Kubernetes, blockchain и формальной верификации. А потом выясняется, что можно просто посмотреть под стол.

Не каждый простой способ является костылём.

Иногда это просто способ, который не успели испортить архитекторы.

В итоге мы ускоряли кодек, а нашли проблему в постановке задач

За сессию появилось шесть коммитов: reusable zstd context, переход с level 1 на level 6, полный sweep уровней, исправление retry после отказа, новый approval popup и починка stale test.

Если смотреть на Git — это шесть разных задач.

Если смотреть на смысл — одна.

Каждый раз сначала существовало объяснение проблемы.

И каждый раз полезно было ему не поверить.

«Кодек медленный» оказалось не совсем правдой. Мы использовали библиотеку так, что большая часть времени уходила на подготовку к работе.

«Level 6 слишком дорогой» оказалось правдой только внутри сломанного benchmark.

«Host повторяет запрос» — нет, host вообще работал нормально, повторял client.

«Approval dialog сломан» — нет, актуальная архитектура почти не была с ним соединена.

«Тест падает» — да, только production оказался правее теста.

«Этот binary свежий» — возможно, но лучше проверить.

И именно поэтому я всё больше думаю, что главная работа разработчика сегодня — не писать код.

Код становится дешёвым.

Правильные ответы на неправильные вопросы теперь можно генерировать промышленными объёмами.

Дорогим остаётся другое: понять, какой вопрос вообще надо было задать.

Можно за неделю переписать компрессор.

Можно подключить LZ4.

Можно придумать новый adaptive codec.

Можно распараллелить всё через Rayon.

Можно затащить GPU.

Можно заказать FPGA и почувствовать себя Microsoft Research.

А настоящий фикс всё это время будет лежать в одной строчке:

Не создавай один и тот же объект несколько тысяч раз.

Вот это и есть самое обидное в хорошей оптимизации.

После неё ты выглядишь не как человек, победивший невероятно сложную задачу.

Ты выглядишь как человек, который наконец заметил, что всё это время дверь открывалась в другую сторону.

Зато работает в пятнадцать раз быстрее.

И платят всё равно за результат. =)

Комментарии (2)


  1. Tanner
    29.08.2026 00:55

    Ужин не превращается обратно в рецепт. Он превращается в рвотные массы. Конечно, LLM простительно не знать таких мелочей.


    1. vaalimusic Автор
      29.08.2026 00:55

      Я прошу прощения за себя и за LLM. Я исправлял текст с помощью яндекс GPT. Сам пишу достаточно безграмотно.