Привет, я Артур Валиев, автор EvertyDesk и кодека EVRTCK. Вышла новая версия EvertyDesk Next — 2.1.0. В этот раз хочу не просто перечислить фичи, а рассказать историю одного спора внутри команды, который закончился реальным бенчмарком, а не мнением погромче.
Пару часов назад я писал про то, почему перестал искать папку для заметок и начал искать сообщение — «Заметки как чаты: почему я перестал искать папку и начал искать сообщение». Прочитайте, если ещё не видели, — там та же одержимость, только про другой продукт. Идея была простая: Telegram годами доводил до одержимости то, что большинство продуктов считает косметикой — рендер, отклик, ощущение «мгновенности». EvertyDesk растёт из той же одержимости, только применённой не к сообщениям, а к пикселям на экране: если что-то можно сделать быстрее и честнее, чем «и так сойдёт», это надо сделать.
Команда
EvertyDesk — не результат работы одного человека, хотя коммиты в репозитории по большей части мои. Есть люди, которые тащат это со мной, каждый в своей области, мои близкие люди:
-
Самойленко Евгений — кодеки и game-режим (H264/H265/AV1/EVRTCK в игровом пайплайне).
-
Кобзев Артем — тестировщик, специализируется на провокациях в задержках. Лучший в этом деле, без преувеличения.
-
Валиев Тимур — мой брат, автор EVRT-UDP-протокола, специалист по фреймам и таймингу.
-
Анна Воробьева — обработка пакетов, соавтор AR2R47, работает над EVRT/EVRT2 поверх UDP.
-
Ибрагимов Шамиль — обработка пакетов, поддержка и отладка EVRTCK.
-
Чураев Александр — криптография в видеокодеках и zstd-сжатие.
Без них половины того, что ниже, просто не было бы в таком виде.
Что нового в 2.1.0
Консоль VirtualBox VRDE. До этой версии evertydesk-rdp-viewer умел подключаться только к Hyper-V Enhanced Session. Теперь — и к VirtualBox VRDE тоже, через общий модуль vm_console_runtime, который абстрагирует обе консоли за одним и тем же интерфейсом. Тот уровень паритета с гипервизорами, который был у старого egui-клиента, только на новом стеке — Iced поверх WGPU, без компромиссов по производительности рендера.
Постоянный пароль из Windows Credential Manager. Если в конфиге пароль пустой, клиент пробует достать его из системного хранилища учётных данных через CredReadW. Меньше поводов держать пароль открытым текстом в конфиг-файле на диске.
Debug-тумблеры сети. ignore_lan_candidates и force_relay — для диагностики, когда нужно принудительно прогнать сессию через релей и исключить прямое LAN-соединение из уравнения при разборе проблем с подключением.
Докажи, что EVRTCK быстрее VNC
Внутри команды случился спор: EVRTCK (наш собственный лосслесс-кодек, тайловый XOR-diff + zstd) или VNC — что лучше для support-режима. Спор дошёл до «давай просто поднимем VNC-сервер и сравним». Я прошёл через боль настройки RealVNC — регистрация, лишнее облако, отдельная история, — и в итоге решил вопрос иначе: не гонять живой трафик через два разных сервера, а честно сравнить протоколы на одних и тех же синтетических данных.
Методика
Взял реальный production-энкодер EVRTCK — тот же код, что в проде, без единой модификации под тест — и написал по спецификации RFC 6143 §7.7.4 энкодер Hextile, классической, полностью специфицированной VNC-кодировки. Специально не Tight и не ZRLE: у них есть настраиваемые параметры (JPEG-качество, уровень zlib), результат зависит от эвристик конкретного сервера, и его нельзя «правильно реализовать» без гадания на чужой конфигурации. Hextile — фиксированный битовый формат, корректная реализация однозначна.
Прогнал оба кодека на одинаковых синтетических кадрах 1920×1080: сплошной цвет, разбросанные точечные правки, сплошные блоки правок, шум — та же матрица сценариев, что уже используется в нашем регулярном бенче EVRTCK, не подгонянная под этот спор задним числом.
Реализацию Hextile сверил напрямую с текстом RFC — и нашёл у себя реальную неточность: фон не должен «переноситься» через Raw-тайл, у меня было наоборот. Поправил, перепрогнал — цифры не изменились ни на байт, потому что в тестовых сценариях эта ситуация просто не возникает. Упоминаю это не для красоты, а потому что «я сверился со спекой и нашёл у себя баг» — единственный честный способ дать цифры, которым можно доверять.
Результат — без прикрас
|
Сценарий |
EVRTCK |
Hextile (VNC) |
Кто быстрее |
|---|---|---|---|
|
keyframe, сплошной цвет |
14 555 Б |
8 180 Б |
VNC, в 1.8 раза |
|
keyframe, градиент |
585 КБ |
8.3 МБ |
EVRTCK, в 14 раз |
|
сплошной блок правок, 5–90% |
989–13 127 Б |
512–7 472 Б |
VNC, в ~1.7–2 раза |
|
разбросанные правки, 5–50% |
989–7 415 Б |
2 044–20 404 Б |
EVRTCK, в 2–2.75 раза |
|
шум/видео, 5–90% |
357К–6.45М |
414К–7.49М |
EVRTCK, стабильно на 16% |
Keyframes на трёх разрешениях (720p/1080p/4K)
|
Сценарий |
EVRTCK |
Hextile |
Кто быстрее |
|---|---|---|---|
|
solid, 720p |
6 575 Б |
3 620 Б |
VNC, в 1.8 раза |
|
solid, 1080p |
14 555 Б |
8 180 Б |
VNC, в 1.8 раза |
|
solid, 4K |
58 160 Б |
32 420 Б |
VNC, в 1.8 раза |
|
gradient, 720p |
356 КБ |
3.69 МБ |
EVRTCK, в 10.3 раза |
|
gradient, 1080p |
585 КБ |
8.3 МБ |
EVRTCK, в 14.2 раза |
|
gradient, 4K |
1.49 МБ |
33.2 МБ |
EVRTCK, в 22.3 раза |
Первый кадр сессии — сравнивать не с чем, кодек должен описать всё с нуля. На сплошном цвете соотношение держится ровно 1.8× на всех разрешениях — это чисто структурный эффект (у Hextile «перенос фона» стоит 1 байт на тайл, у EVRTCK своя фиксированная цена за тайл, отношение не зависит от масштаба). А вот на градиенте (нет одинаковых пикселей вообще) разрыв растёт с разрешением — 10× на 720p, 14× на 1080p, 22× на 4K. Это ключевой момент: zstd у EVRTCK ищет структуру и находит её даже там, где её визуально «не видно» (градиент технически предсказуем — соседние пиксели близки по значению), а Hextile на несовпадающих пикселях скатывается в Raw без какого-либо сжатия вообще. Чем больше кадр — тем дороже платит VNC за отсутствие компрессора.
P-frames — недостающий сценарий: сплошной блок шума
Раньше «шум» тестировался только в разбросанном виде — точечные случайные изменения по всему экрану. Не хватало кейса «сплошной блок шума» — а это ровно то, что происходит, когда в углу экрана крутится видео или воспроизводится анимация в одном окне.
|
Сценарий |
EVRTCK |
Hextile |
Кто быстрее |
|---|---|---|---|
|
сплошной блок шума, 5% |
361 439 Б |
418 296 Б |
EVRTCK, на 16% |
|
сплошной блок шума, 15% |
1 083 844 Б |
1 254 840 Б |
EVRTCK, на 16% |
|
сплошной блок шума, 50% |
3 612 293 Б |
4 182 016 Б |
EVRTCK, на 16% |
|
сплошной блок шума, 90% |
6 502 091 Б |
7 527 720 Б |
EVRTCK, на 16% |
Цифры почти идентичны разбросанному шуму (тоже стабильно ~16% в пользу EVRTCK). Объяснимо: на шуме Hextile всё равно падает в Raw-фолбэк на каждом тайле — что рядом с другим шумным тайлом, что нет, скидки не будет (RFC прямо говорит: Raw-тайл разрывает перенос состояния). Поэтому «разбросанность» правок для VNC на шумном контенте почти не играет роли — 12 байт заголовка прямоугольника тонут в мегабайтах Raw-данных. А вот на однотонных правках разница между сплошным блоком и разбросанными точками огромная — там, где Hextile реально умеет экономить, расположение решает всё.
А что насчёт скорости, а не только размера?
Тут я сам себя поймал за руку. Весь спор был про «EVRTCK быстрее» — а я до этого момента мерил только размер пакета, ни разу не время энкодинга. Это не одно и то же: маленький пакет не значит «быстро сжали», это может значить и «долго искали, зато нашли компактную форму».
Расширил инструмент — добавил Hextile-энкодер, который реально пишет байты (не просто считает размер), и замерил wall-clock время против настоящего EvrtckEncoder::encode(). 200 итераций на сценарий, медиана, 20 прогонов на разогрев не считаются.
Первый прогон дал результат, который выглядел слишком хорошо, чтобы быть правдой: на static_0pct (кадр вообще не изменился) Hextile показал 0.1 микросекунды против 892 у EVRTCK — почти 8000-кратное преимущество. Я не поверил и полез разбираться. Причина оказалась в самой методике: я скармливал Hextile-энкодеру уже готовый список «какие тайлы изменились» — тот же список, который использовала функция генерации тестовых кадров. А EvrtckEncoder::encode() сам сканирует весь кадр, чтобы этот список построить. Получалось «EVRTCK: найди изменения + сожми» против «Hextile: тебе уже сказали что менялось, просто отформатируй байты». Нечестно — настоящий VNC-сервер тоже обязан сам сканировать фреймбуфер, никто не подсказывает ему, что изменилось.
Добавил detect_dirty_tiles() — честное посайтловое сравнение текущего кадра с предыдущим, на той же сетке 32×32, что использует сам EVRTCK. Теперь обе стороны платят за обнаружение изменений одинаково, и вот тогда цифры стали осмысленными:
|
Сценарий |
EVRTCK |
Hextile |
Кто быстрее |
|---|---|---|---|
|
static_0pct |
892 мкс |
2 891 мкс |
EVRTCK, в 3.2 раза |
|
сплошной блок правок, 5–90% |
1 770–2 438 мкс |
3 300–3 868 мкс |
EVRTCK, в 1.5–2 раза |
|
разбросанные правки, 5–50% |
1 714–1 876 мкс |
3 711–4 577 мкс |
EVRTCK, в 2.2–2.4 раза |
|
шум/видео разбросанный, 5–90% |
5 190–67 947 мкс |
3 770–6 488 мкс |
Hextile, в 1.4–10.5 раза |
|
шум/видео сплошной, 5–90% |
6 075–70 214 мкс |
3 490–5 383 мкс |
Hextile, в 1.7–13 раз |
И вот тут спор внутри команды закрылся не победой, а честным компромиссом. На структурированном контенте — там же, где EVRTCK проигрывал по размеру пакета — он оказывается быстрее по времени, в 1.5–3 раза. А на шуме и видео — там, где EVRTCK выигрывал по размеру на стабильные 16% — он медленнее, и разрыв растёт вместе с процентом шума, до 13 раз на 90%.
Объяснение прямое: zstd тратит реальное CPU-время, пытаясь найти структуру даже там, где её почти нет (случайный шум почти несжимаем по определению), и эта работа окупается скромной экономией байт — но стоит времени. Hextile на шуме просто копирует пиксели без попытки сжать — быстро писать, но дороже передавать по сети.
Так что если сузить вопрос до «байт меньше» — EVRTCK выигрывает там, где это важно (реалистичный контент). Если сузить до «закодировал быстрее» — ответ меняется на противоположный ровно в тех же категориях, где менялся победитель по размеру. Оба утверждения честные, оба воспроизводимые, и оба нужны в статье — иначе это будет ровно тот вид «маркетинга с цифрами», от которого я и хотел уйти, затевая всё это вместо холивара на словах.
Дальше
В бэклоге — два конкретных, а не абстрактных пункта. Первый: carry-forward оптимизация для однотонных соседних грязных тайлов в EVRTCK, чтобы закрыть тот самый проигрыш по размеру на сплошных блоках правок. Второй: то же самое сравнение, только против Tight и ZRLE — раз инфраструктура для честного бенчмарка теперь есть, глупо останавливаться на одном Hextile.
Код и методика открыты целиком: cargo run --release --bin vnc_hextile_bench, замер времени и детектор изменений задокументированы прямо в файле src/bin/vnc_hextile_bench.rs.
ссылка на оригинал статьи https://habr.com/ru/articles/1070712/