Я прийняв виклик: побудувати відеопровід YOLOv8 в режимі реального часу за допомогою Vanilla ONNX Runtime. Немає набряканих фреймворків. Немає Python bottlenecks. Просто сирий C++ grit. Але якщо ви намагаєтеся транслювати відео H.264 в прямому ефірі через нейронну мережу в масштабі на крайньому обладнанні? Global Interpreter Lock (GIL) Python і його патологічна одержимість копіюванням пам'яті є яскравими відповідальністю. Нещодавно мені поставили просту задачу: створити швидке висновок для потоку відео, використовуючи час запуску ONNX ванілі та модель сегментації YOLOv8. Ось як я перетягнув повільний прототип 10 FPS в твердому 29 FPS звіра, і "останній бос" помилки, які я повинен був вбити в дорозі. In reality, it was a journey through engineering hell. (Повний вихідний код для мазохістів: відео-йоло-даш-процесор) Відео-йоло-даш процесор The FogAI Sandbox: Валідація перед інтеграцією Цей репозиторій не є самостійною іграшкою - це Я використовую це середовище, щоб ретельно випробувати конкретні моделі комп'ютерного зору, конструкції двигунів та моделі оптимізації, перш ніж вони будуть просунуті до . dedicated testbed FogAI core Якщо стратегія (наприклад, хардверне мапірування Zero-Copy) не може вижити тут при 29 FPS, вона не має бізнесу, перебуваючи всередині промислової автономної нервової системи. Previous Chapters in the FogAI Saga: The Manifesto: Prompts Are Overrated.Here's How I Built Zero-Copy Fog AI Node Без Python Історія кар'єри: Шанси переоцінені: я побудував нульовий вузол AI без Python (І це болить) Джерело страждань: GitHub: NickZt/FogAi Податок на копію пам'яті (Memory Copy Tax) Більшість прототипів комп'ютерного зору повільні, тому що вони розглядають пам'ять як гру Гарячої картоплі. Моя початкова архітектура була «стандартною» безладою: FFmpeg розшифрував H.264 у формати YUV, перетворив його на OpenCV. (BGR) для живлення моделі, нанесення масок на зображення RGB, перетворення його до YUV, і нарешті потрапив на кодувальник. cv::Mat назад На процесорі ARM, що обробляє кадри 4K, ці кадри горять до Тільки переміщаючи біт навколо. That's three unnecessary memory copies and two heavy pixel-format conversions. 30% of your cycles Ми вирішили це, впроваджуючи Замість того, щоб перетворювати рамки, я мапував Y-plane (Luminance) безпосередньо в OpenCV Вантажівка Zero-Copy Hardware Mapping AVFrame cv::Mat С++ // Mapping the hardware Y-plane natively - zero memcpy, zero overhead. cv::Mat y_plane(yuvFrame->height, yuvFrame->width, CV_8UC1, yuvFrame->data, yuvFrame->linesize); // YOLO segmentation masks now inject binary modifications directly // onto the hardware Y sequence. y_plane(bbox).setTo(0, valid_mask); Обійшовши конверсію, я повністю пропустив пляшечку процесора, але я все ще був обмежений 23 FPS. Why? Мутабельність і асинхронний перерозподіл Профілювання показало, що мої нитки були замкнені в послідовному смертельному утриманні. абстракція ґрунтується на мутації спільних внутрішніх буферів.Якщо я просто породив більше ниток на одній моделі, вони забруднювали один одного, і система зламалася. YOLO Виявилося, що конкурентний басейн Моделі - один унікальний екземпляр моделі ONNX на нитку робітника. The Fix: std::unique_ptr<YOLO_Segment> Але був улов: Оскільки працівники закінчують в різні часи, Frame 2 може закінчитися раніше Frame 1, викликаючи відео, щоб забитися, як 90-х стрибок. Забезпечує безперебійну синхронізацію H.264. DASH video requires strict frame order. std::map С++ // Reorder buffer logic to keep the stream sequential std::map<int64_t, FramePayload> reorderBuffer; int64_t expected_pts = 0; while (true) { auto payload = inferenceQueue.pop(); // Workers drop processed frames here reorderBuffer[payload.pts] = payload; // Emit frames only when the sequential timestamp flags align while (!reorderBuffer.empty() && reorderBuffer.begin()->first == expected_pts) { auto it = reorderBuffer.begin(); encoder.writeFrame(it->second.yuvFrame, it->second.pts); reorderBuffer.erase(it); expected_pts++; } } Фінальний босс: Thread Cache Thrashing На папері логіка була досконалою. на практиці мої FPS впали до Мої затримки Time-To-Inference (TTI) зросли з 43 мс до жахливих 890 мс. 10 FPS I was a victim of CPU Cache Thrashing. Незважаючи на те, що я роз'єднав свої замки, основні бібліотеки ML (OpenCV і ONNX) «допомагали» мені, породжуючи свої власні внутрішні нитки. ONNX Runtime: За замовчуванням до hardware_concurrency() / 2 нитки на сесію. З 10 співробітників, він породив 100+ внутрішніх ниток на моєму 20-ядерному процесорі. OpenCV: автоматично розміщує працівників для операцій, таких як .setTo(). Мої призначені працівники боролися з нитками ONNX, які боролися з нитками OpenCV. Thousands of context switches were destroying my L1/L2 caches every second. Виправлення було жорстоким «ні» до недвозначної співпраці. я позбавив бібліотеки свого права породжувати нитки: С++ int main() { // Globally disable implicit OpenCV threading cv::setNumThreads(1); // Cap ONNX Runtime to a single thread per op Ort::SessionOptions session_options; session_options.SetIntraOpNumThreads(1); session_options.SetInterOpNumThreads(1); } Шум перемикання контексту зник. Кеш інструкції мого процесора синхронізується. трубопровід відразу ж вдарив до бездоганного Розмір TTI становить 329 мс. 29 FPS Підтримка над его: стратегія ванілі Звичайне питання, яке я отримую, це: "Якщо ви так зосереджені на продуктивності, чому б не викрутити двигун і оптимізувати ядра самостійно?" Відповідь є Technical Debt avoidance. Якщо ви хакнете внутрішні записи двигуна, ви підписуєтеся на безкінечний цикл технічного обслуговування.Кожен раз, коли нова версія падає з підтримкою свіжого апаратного забезпечення---як (57 % від загальної суми) або --- вам доведеться вручну переносити свої налаштовані оптимізації. , Я можу "захопити" ці оновлення обладнання безкоштовно, просто потрапивши в номер версії. ARM KleidiAI Intel DL Boost (VNNI) Vanilla Inference Engine Аналогічно, я вирішив не оптимізувати трубопровід кодування / декодування. Чому? Тому що постачальники апаратного забезпечення вже зробили це. або The , ці чіпи мають прискорення на рівні кремнію для H.264. · залишити кодек до металу, для якого він був побудований. Intel QuickSync Rockchip VPU Zero-Copy Bridge Висновок: припинити гадати, почати профілювання Скасування AI для реального світу вимагає відшарування шарів абстракції, з якими ми занадто комфортно стикаємося. Якщо у вас є завдання з важкими навантаженнями тензора на відео: Убийте конверсії пікселів - працюйте на апаратних планах безпосередньо. Ізолюйте свої моделі - один екземпляр на працівника. Перезавантажити послідовні виходи - не дозволяйте асинхронним кінцевим часом переривати ваш потік. Ніколи не дозволяйте вашим бібліотекам породжувати власні нитки. Залишайтеся Ванілою - оптимізуйте свою архітектуру, а не двигун, щоб тримати технологічний борг низьким. Наступна статтяЧому ми готуємося? → Для нульової копії... Grounding DINO