Acepté un desafío: construir una pipa de vídeo YOLOv8 en tiempo real usando Vanilla ONNX Runtime. Sin marcos hinchados. Sin barreras de botella de Python. Solo crudo C++ grit. Pero si estás tratando de transmitir vídeo en vivo H.264 a través de una red neural a escala en hardware de punta? el Lock de Interpreter Global de Python (GIL) y su obsesión patológica con la copia de memoria son responsabilidades brillantes. Recientemente me fue dada una tarea simple: crear inferencias rápidas para un flujo de vídeo utilizando un runtime ONNX de vainilla y un modelo de segmentación YOLOv8. Aquí está cómo arrastré un lento prototipo de 10 FPS en una bestia de 29 FPS sólida en roca, y los errores "finales" que tuve que matar a lo largo del camino. In reality, it was a journey through engineering hell. (Código fuente completo para los masochistas: video-yolo-dash-procesador) Procesador de vídeo yolo-dash The FogAI Sandbox: Validación antes de la integración Este repositorio no es un juguete independiente - es un Utilice este entorno para probar estrictamente los modelos de visión de ordenador específicos, las construcciones de motores y los patrones de optimización antes de que se promuevan a la empresa. . dedicated testbed FogAI core Si una estrategia (como el mapeo de hardware Zero-Copy) no puede sobrevivir aquí a 29 FPS, no tiene negocio dentro de un sistema nervioso autónomo industrial. Previous Chapters in the FogAI Saga: El Manifiesto: Prompts están sobrevalorados. aquí es cómo construí un nodo de inteligencia artificial de niebla de copia cero sin Python La historia de carrera: Prompts están sobrevalorados: Construí un nodo de inteligencia artificial de nube de copia cero sin Python (y me duele) La fuente del sufrimiento: GitHub: NickZt/FogAi La trampa del "impuesto de copia de memoria" La mayoría de los prototipos de visión por ordenador son lentos porque tratan la memoria como un juego de patatas calientes. Mi arquitectura inicial fue la confusión "estándar": FFmpeg decodificó H.264 en formatos de hardware YUV, lo convirtió en un OpenCV (BGR) para alimentar el modelo, aplicó máscaras en la imagen RGB, la convirtió en YUV, y finalmente golpeó el codificador. cv::Mat Regreso En una CPU ARM que procesa cuadros 4K, que sobre la cabeza se quema hasta Sólo se mueven los bits alrededor. That's three unnecessary memory copies and two heavy pixel-format conversions. 30% of your cycles Esto se consiguió implementando En lugar de transformar el marco, mapeé el hardware Y-plane (Luminance) directamente en un OpenCV El Wrapper. Zero-Copy Hardware Mapping AVFrame cv::Mat 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); Al pasar por alto la conversión, salí del botellín de la CPU por completo, pero todavía estaba limitado a 23 FPS. Why? Mutabilidad y reordenamiento asíncrono El perfilado mostró que mis hilos estaban encerrados en una secuencia de muerte. La abstracción se basa en la mutación de los buffers internos compartidos.Si yo simplemente engendré más hilos en un único modelo, se contaminaron entre sí, y el sistema se segfaultó. YOLO Hemos creado un grupo competitivo de modelos---una instancia única de modelo ONNX por hilo de trabajador. The Fix: std::unique_ptr<YOLO_Segment> Pero hubo una captura: Dado que los trabajadores terminan en diferentes momentos, Frame 2 podría terminar antes de Frame 1, causando que el vídeo se estrellara como un salto de los años 90. Para una sincronización H.264 perfecta. DASH video requires strict frame order. std::map 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++; } } El jefe final: Thread Cache Thrashing En el papel, la lógica era perfecta. En la práctica, mi FPS se redujo a Mis latencias de Time-To-Inference (TTI) se dispararon de 43ms a un horrible 890ms. 10 FPS I was a victim of CPU Cache Thrashing. A pesar de que había desconectado mis cerraduras, las bibliotecas ML subyacentes (OpenCV y ONNX) estaban "ayudándome" al engendrar sus propios hilos internos. ONNX Runtime: Defaults to hardware_concurrency() / 2 threads per session. Con 10 trabajadores, engendró 100+ threads internos en mi CPU de 20 núcleos. OpenCV: Despliega automáticamente a los trabajadores para operaciones como .setTo(). Mis trabajadores designados estaban luchando contra los hilos de ONNX, que estaban luchando contra los hilos de OpenCV. Thousands of context switches were destroying my L1/L2 caches every second. La corrección fue un brutal "No" a la concurrencia implícita. privé a las bibliotecas de su derecho a engendrar hilos: 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); } El ruido de cambio de contexto desapareció. Mi caché de instrucciones de la CPU se sincroniza. con un techo TTI de ~329ms. 29 FPS Maintenance Over Ego: La estrategia de la vainilla Una pregunta común que recibo es: "Si usted está tan enfocado en el rendimiento, ¿por qué no forjar el motor y optimizar los núcleos usted mismo?" La respuesta es Technical Debt avoidance. Si hackeas los internos del motor, te registras para un ciclo de mantenimiento sin fin. Cada vez que una nueva versión cae con soporte para hardware fresco---como (57% de impulso de prefijo) o --- tendría que re-portar sus optimizaciones personalizadas manualmente. , Puedo "catch" estas actualizaciones de hardware de forma gratuita simplemente haciendo clic en un número de versión. ARM KleidiAI Intel DL Boost (VNNI) Vanilla Inference Engine De la misma manera, escogí no optimizar el tubo de codificación/decodificación. ¿Por qué? Porque los proveedores de hardware ya lo hicieron. O el , estos chips tienen aceleración de nivel de silicio para H.264. Dejar los codecs al metal para el que fueron construidos. Intel QuickSync Rockchip VPU Zero-Copy Bridge Conclusión: Deja de adivinar, empieza a perfilar Escalar la IA para el mundo real requiere despejar las capas de abstracción con las que nos sentimos demasiado cómodos.Python oculta estos impuestos de latencia hasta que puedas poner el sistema en producción. Si se le encarga con cargas de carga pesadas de tensores en el vídeo: Matar las conversiones de píxeles---trabajar en los planos de hardware directamente. Isole sus modelos - una instancia por trabajador. Reordenar las salidas secuenciales---no dejes que los tiempos de finalización asíncronos rompan tu flujo. Nunca dejes que tus bibliotecas engendren sus propios hilos. Quédate con Vanilla---optimiza tu arquitectura, no el motor, para mantener la deuda tecnológica baja. Siguiente ¿Estamos preparando el nodo FogAI? Para una copia cero... Grounding DINO