Issue
The video rendering pipeline was inefficiently utilizing only one CPU core despite running on a system with 32 cores. This underutilization caused performance bottlenecks during frame processing and FFmpeg encoding, particularly for high-resolution video generation. Key operations, such as frame saving and encoding, were processed sequentially, limiting overall throughput.
Culprit
The inefficiency stemmed from three single-threaded operations in the pipeline:
- Frame saving: PNG files were saved sequentially using single-threaded input/output operations.
- FFmpeg commands: Default single-threaded execution for encoding, mixing, and multiplexing (muxing) operations.
- Encoding preset: The default
-preset mediumprioritized compression over speed, further exacerbating the bottleneck.
Solution
Parallel Frame Processing
The frame-saving process was parallelized using Python’s multiprocessing.Pool to distribute I/O operations across all available CPU cores. This resulted in up to 32x faster frame saving on a 32-core system by leveraging concurrent processing.
FFmpeg Multithreading
To optimize FFmpeg operations, the -threads parameter was dynamically set to match the number of available CPU cores. This was applied to:
- Video encoding
- Audio mixing
- Audio/video multiplexing
This adjustment ensured full CPU utilization during all phases of the rendering pipeline.
Encoding Optimization
The H.264 encoding preset was changed from medium (default) to fast to prioritize encoding speed over compression. While this introduced a minor tradeoff in file size (~5-8% increase), it significantly improved rendering performance.
Performance Results
- Frame saving: Achieved near-linear scaling with available CPU cores.
- FFmpeg encoding: Full utilization of all CPU resources during encoding, mixing, and muxing phases.
- Overall rendering: Substantial improvement in throughput, with near-linear scaling in performance relative to core count.