Ho accettato una sfida: costruire una pipeline video YOLOv8 in tempo reale utilizzando Vanilla ONNX Runtime. nessun framework gonfiato. nessun bottlenecks Python. Ma se stai cercando di trasmettere in diretta video H.264 attraverso una rete neurale su scala su hardware edge? il Global Interpreter Lock (GIL) di Python e la sua ossessione patologica alla copia della memoria sono responsabilità brillanti. Recentemente mi è stato affidato un obiettivo semplice: creare una rapida inferenza per un flusso video utilizzando un runtime ONNX di vaniglia e un modello di segmentazione YOLOv8. Ecco come ho trascinato un prototipo lento a 10 FPS in una bestia di 29 FPS solida in roccia, e i bug "final boss" che ho dovuto uccidere lungo il percorso. In reality, it was a journey through engineering hell. (Codice sorgente completo per i masochisti: video-yolo-dash-processore) Processore video yolo-dash The FogAI Sandbox: Validazione prima dell'integrazione Questo repository non è un giocattolo indipendente – è un Utilizzo questo ambiente per testare rigorosamente i modelli specifici di visione del computer, le costruzioni del motore e i modelli di ottimizzazione prima che vengano promossi alla . dedicated testbed FogAI core Se una strategia (come la mappatura hardware Zero-Copy) non può sopravvivere qui a 29 fps, non ha affari di essere all'interno di un sistema nervoso autonomo industriale. Previous Chapters in the FogAI Saga: Il Manifesto: Prompts sono sopravvalutati: ecco come ho costruito un nodo AI a nebbia zero senza Python La storia della carriera: Prompts sono sopravvalutati: ho costruito un nodo AI a nebbia zero senza Python (e fa male) La fonte della sofferenza: GitHub: NickZt/FogAi La trappola della “tax of memory copy” La maggior parte dei prototipi di visione computerizzata sono lenti perché trattano la memoria come un gioco di patate calde. La mia architettura iniziale era la confusione "standard": FFmpeg decodificò H.264 in formati hardware YUV, convertendolo in un OpenCV (BGR) per alimentare il modello, applicato maschere sull'immagine RGB, convertito a YUV, e infine colpire l'encoder. cv::Mat Ritorno Su una CPU ARM che elabora quadri 4K, che brucia fino a Spostare i bit intorno. That's three unnecessary memory copies and two heavy pixel-format conversions. 30% of your cycles Questo è stato risolto implementando Invece di convertire il frame, ho mappato il hardware Y-plane (Luminance) direttamente in un OpenCV di Wrapper. Zero-Copy Hardware Mapping AVFrame cv::Mat di C++ // 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); Oltre alla conversione, ho saltato completamente il bottleneck della CPU, ma ero ancora limitato a 23 FPS. Why? Mutabilità e riordine asincrono Profiling ha mostrato che i miei fili sono stati bloccati in un sequenziale death grip. L'astrazione si basa sulla mutazione dei bufferi interni condivisi.Se ho appena generato più fili su un singolo modello, si contaminano l'un l'altro e il sistema si separa. YOLO Si tratta di un pool concorrenziale di modelli---una singola istanza del modello ONNX per filo lavoratore. The Fix: std::unique_ptr<YOLO_Segment> Ma c’è stata una cattura: Poiché i lavoratori finiscono in tempi diversi, Frame 2 potrebbe finire prima di Frame 1, causando il video a sbattere come un taglio salto degli anni '90. per garantire la sincronizzazione H.264 senza difetti. DASH video requires strict frame order. std::map di C++ // 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++; } } Titolo originale: The Final Boss: Thread Cache Thrashing Sulla carta, la logica era perfetta. In pratica, il mio FPS si è schiantato Le mie latenze Time-To-Inference (TTI) sono salite da 43ms a un orribile 890ms. 10 FPS I was a victim of CPU Cache Thrashing. Anche se avevo disconnesso le mie serrature, le biblioteche ML sottostanti (OpenCV e ONNX) mi stavano "aiutando" generando i loro fili interni. ONNX Runtime: Defaults to hardware_concurrency() / 2 thread per sessione. Con 10 lavoratori, ha generato 100+ thread interni sulla mia CPU a 20 core. OpenCV: distribuisce automaticamente i lavoratori per operazioni come .setTo(). I miei dipendenti designati stavano combattendo i thread di ONNX, che stavano combattendo i thread di OpenCV. Thousands of context switches were destroying my L1/L2 caches every second. La correzione era un brutale "No" alla concorrenza implicita. ho privato le biblioteche del loro diritto di generare fili: di C++ 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); } Il rumore di commutazione del contesto è scomparso. Il cache delle istruzioni della CPU è sincronizzato. Il pipeline ha subito colpito un con un tetto TTI di ~329ms. 29 FPS Maintenance Over Ego: La strategia della vaniglia Una domanda comune che mi viene posta è: "Se sei così focalizzato sulle prestazioni, perché non forcare il motore e ottimizzare i kernel da solo?" La risposta è Technical Debt avoidance. Se si hackerano gli interni del motore, si sta registrando per un ciclo di manutenzione senza fine. ogni volta che una nuova versione cade con il supporto per il hardware fresco---come (57% di impulso di prefisso) o --- dovresti re-portare le tue ottimizzazioni personalizzate manualmente. , Posso "catturare" questi aggiornamenti hardware gratuitamente semplicemente colpendo un numero di versione. ARM KleidiAI Intel DL Boost (VNNI) Vanilla Inference Engine Allo stesso modo, ho scelto di non ottimizzare il pipeline di codifica / decodifica. Perché? Perché i fornitori di hardware lo hanno già fatto. O il , queste chip hanno accelerazione a livello di silicio per H.264. lasciare i codec al metallo per il quale sono stati costruiti. Intel QuickSync Rockchip VPU Zero-Copy Bridge Conclusione: smettere di indovinare, iniziare a profilare Scalare l'IA per il mondo reale richiede di ripulire gli strati di astrazione con cui siamo diventati troppo a proprio agio. Se sei incaricato di pesanti carichi di tensore su video: Uccidere le conversioni di pixel---lavorare direttamente sui piani hardware. Isolate i vostri modelli--una istanza per lavoratore. Reordinare le uscite sequenziali---non lasciare che i tempi di fine async interrompano il flusso. Non lasciate mai che le vostre biblioteche generino i loro fili. Stay Vanilla---ottimizza la tua architettura, non il motore, per mantenere il debito tecnologico basso. Prossimo per il nodo FogAI? stiamo preparando Per una copia zero... Grounding DINO