История о том, как виртуализация может украсть у тебя инструкции процессора, оставив в логах обманчивое «всё в порядке».
Контейнерный детектор Frigate тормозил. Причем тормозил безнадёжно: один кадр обрабатывался за 600–750 миллисекунд. Для системы видеонаблюдения это приговор - ты видишь движение на записи тогда, когда объект уже давно покинул кадр. Процессор Intel i9-9900K в теории должен был щелкать такие задачи, но на деле превратился в медленную калькуляторную машину.
Самое странное - нагрузка была распределена криво. Docker-VM забирала 14 ядер хоста, но сам детектор упирался в потолок и не мог разогнаться.
Проброс, которого не было
Первым делом полез в настройки проброса. GPU-passthrough в Docker Desktop на macOS - это миф, так что Intel UHD 630 был недоступен. USB-акселераторы (Coral TPU) тоже не достучаться до контейнера из-за ограничений виртуализации.
Логика была простой: «раз нет ускорителя, работаем на CPU». Я перебирал модели, менял веса, пытался оптимизировать конфиг. Все приборы рапортовали: ресурсы есть, память свободна, ядра проброшены. Но задержка в 700 мс не шевелилась. Казалось, что я упёрся в физический предел виртуализации - налог на слой абстракции между macOS и Linux.
Я был уверен, что проблема в «оверхеде» виртуальной машины. Оказалось, всё гораздо приземлённее и глупее.
Один флаг в /proc/cpuinfo
Когда привычные рычаги не сработали, я скормил симптомы ИИ. Не просил «починить», а попросил проанализировать разрыв между мощностью i9 и реальным временем инференса. Ответ пришёл в виде одного технического уточнения:
«Проверь, какие именно инструкции CPU видит гость. Возможно, виртуализатор маскирует AVX2».
Это был тот самый «тупой» вопрос, до которого я не додумался, потому что считал виртуализацию прозрачной. Я зашёл в контейнер и выполнил
cat /proc/cpuinfo
В списке флагов было avx, но отсутствовал avx2.
Хост-процессор i9 поддерживает AVX2. Но Apple Virtualization Framework (современный бэкенд Docker) по какой-то причине маскировал эти инструкции от гостевой ОС. Библиотеки tflite и OpenVINO, увидев отсутствие AVX2, молча откатывались на старый добрый скалярный режим или AVX1.
Это не был «налог на виртуализацию». Это была работа вполсилы. Машина имела мышцы, но ей завязали руки.
Шаг назад, к HyperKit
Решение потребовало возврата в прошлое. Нужно было переключить Docker с современного Apple-VZ на старый HyperKit, который, несмотря на свою заброшенность, пробрасывал AVX2 честно.
Просто переключить галочку в настройках не получилось - VM выдавала ошибку при загрузке, так как диск, созданный новым фреймворком, был несовместим со старым. Пришлось идти по пути полного сноса:
1. Выключить Docker
2. Удалить (переименовать) файл виртуального диска:
~/Library/Containers/com.docker.docker/Data/vms/0
3. В settings-store.json выставить:
"UseVirtualizationFramework": false
После этого HyperKit собрал чистый диск, загрузился и - наконец-то - показал в /proc/cpuinfo заветный флаг avx2.
Осталось сменить бэкенд детектора во Frigate на OpenVINO. Результат оказался почти магическим: время инференса упало с 570 мс до 15 мс. Нагрузка на процессор в мониторинге Docker упала с нескольких ядер до почти нулевых значений.
# Конфиг детектора после фикса
detectors:
ov:
type: openvino
device: CPU
Виртуализация - не зеркало, а фильтр
Эта история оставила после себя горький привкус «тёплой изнанки» прогресса. Новые инструменты (Apple-VZ) работают стабильнее и быстрее в общих задачах, но они же создают новые, невидимые стены.
Это была ловушка собственного доверия к абстракции. Казалось: раз виртуальной машине выделено 16 ядер i9 - значит, она получит всю мощь этого кремния. Но виртуализация - не зеркало, а фильтр. И иногда этот фильтр отсекает именно то, на чём держится производительность конкретного приложения.
Мораль здесь не про Docker или AVX2. Когда система ведет себя странно, первым делом нужно проверять не то, что «должно работать», а те допущения, которые ты перестал подвергать сомнению.
ИИ здесь оказался полезен парадоксально: он не знал моей конфигурации, но знал, где в подобных стеках обычно прячутся «дыры». Он просто напомнил мне, что между железом и кодом всегда есть кто-то третий, кто может решать за тебя, какие инструкции сегодня будут доступны.
