EvertyDesk Next 2.1.0: VirtualBox VRDE и честный ответ на вопрос «а EVRTCK точно быстрее?»

от автора

Привет, я Артур Валиев, автор 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/