Приех предизвикателство: изграждане на видео тръбопровод в реално време YOLOv8 с помощта на Vanilla ONNX Runtime. Няма надути рамки. Няма Python bottlenecks. Просто суров C++ grit. Нека бъдем честни: Python е безспорният крал на изследователската лаборатория. но ако се опитвате да предавате на живо H.264 видео чрез невронна мрежа в мащаб на ръчния хардуер? Наскоро ми беше възложена проста задача: да създам бързо заключение за видео поток, като използвам ванилия ONNX runtime и 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 са надценени. ето как изградих AI възел за нулево копиране на мъгла без Python Историята на кариерата: Възможностите са надценени: Създадох нулево копие на AI възел за мъгла без Python (и боли) Източникът на страданието: GitHub: NickZt/FogAi Данъкът върху „паметните копия“ Повечето прототипи на компютърното зрение са бавни, защото третират паметта като игра на горещ картоф. Първоначалната ми архитектура беше "стандартната" бъркотия: 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); Чрез заобикаляне на преобразуването над главата, напълно прескочих бутилката на CPU, но все още бях ограничен до 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++; } } Оригинално име: The Final Boss: Thread Cache Thrashing На хартия логиката беше перфектна.На практика моят FPS се срина до My Time-To-Inference (TTI) латенти скочи от 43ms до ужасно 890ms. 10 FPS I was a victim of CPU Cache Thrashing. Въпреки че разделих ключалките си, основните ML библиотеки (OpenCV и ONNX) ми помогнаха, като създадоха свои собствени вътрешни нишки. ONNX Runtime: По подразбиране до hardware_concurrency() / 2 нишки на сесия. С 10 работници, той породи 100+ вътрешни нишки на моя 20-ядрен CPU. 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 таван от ~329ms. 29 FPS Поддръжка над егото: стратегията на ванилата Един често задаван въпрос, който получавам, е: "Ако сте толкова фокусирани върху производителността, защо да не виете двигателя и сами да оптимизирате ядрата?" Отговорът е Technical Debt avoidance. Ако хакнете вътрешните данни на двигателя, се регистрирате за безкрайна верига за поддръжка.Всеки път, когато нова версия пада с поддръжка за свеж хардуер---като (57% увеличение на предварителното натоварване) или --- ще трябва да пренасочите персонализираните си оптимизации ръчно. , Мога да "заловим" тези хардуерни актуализации безплатно, просто като намеря номер на версията. ARM KleidiAI Intel DL Boost (VNNI) Vanilla Inference Engine По същия начин реших да не оптимизирам тръбопровода за кодиране / декодиране. Защо? Тъй като доставчиците на хардуер вече го направиха. или на , тези чипове имат ускорение на нивото на силиция за H.264. Кодеците се оставят на метала, за който са построени. Intel QuickSync Rockchip VPU Zero-Copy Bridge Заключение: Спрете да гадаете, започнете да профилирате Мащабирането на ИИ за реалния свят изисква отлепване на слоевете абстракция, с които се чувстваме прекалено комфортно. Ако сте натоварени с тежки напречни полезни натоварвания във видеоклипа: Убий конверсии на пиксели - работи директно върху хардуерните планове. Изолирайте моделите си - един случай на работник. Пренареждане на последователни изходи---не позволявайте на асинхронното време на завършване да наруши потока ви. Никога не позволявайте на вашите библиотеки да произвеждат свои собствени нишки. Останете Ванила - оптимизирайте архитектурата си, а не двигателя, за да запазите ниския технологичен дълг. Следваща Следваща публикация: Ние се подготвяме За нулево копиране... Grounding DINO