Benchmark video¶
This example measures the performance of video decoding with different concurrency settings.
It benchmarks spdl.io.load_video() across multiple dimensions:
Various video resolutions (SD: 640x480, HD: 1920x1080, 4K: 3840x2160)
Different worker thread counts (1, 2, 4, 8) for parallel video processing
Different decoder thread counts (1, 2, 4) for FFmpeg’s internal threading
The benchmark evaluates how throughput changes with:
Worker-level concurrency: Number of videos processed concurrently (via
num_workers)Decoder-level concurrency: FFmpeg’s internal threading (via
decoder_options={"threads": "X"})
Example
$ numactl --membind 0 --cpubind 0 python benchmark_video.py --output video_benchmark_results.csv
# Plot results
$ python benchmark_video_plot.py --input video_benchmark_results.csv --output video_benchmark_plot.png
Result
In many cases, when decoding H264 videos, using 2 threads give a good performance.
Source¶
Source
Click here to see the source.
1# Copyright (c) Meta Platforms, Inc. and affiliates.
2# All rights reserved.
3#
4# This source code is licensed under the BSD-style license found in the
5# LICENSE file in the root directory of this source tree.
6
7
8"""This example measures the performance of video decoding with different concurrency settings.
9
10It benchmarks :py:func:`spdl.io.load_video` across multiple dimensions:
11
12- Various video resolutions (SD: 640x480, HD: 1920x1080, 4K: 3840x2160)
13- Different worker thread counts (1, 2, 4, 8) for parallel video processing
14- Different decoder thread counts (1, 2, 4) for FFmpeg's internal threading
15
16The benchmark evaluates how throughput changes with:
17
181. **Worker-level concurrency**: Number of videos processed concurrently (via ``num_workers``)
192. **Decoder-level concurrency**: FFmpeg's internal threading (via ``decoder_options={"threads": "X"}``)
20
21**Example**
22
23.. code-block:: shell
24
25 $ numactl --membind 0 --cpubind 0 python benchmark_video.py --output video_benchmark_results.csv
26 # Plot results
27 $ python benchmark_video_plot.py --input video_benchmark_results.csv --output video_benchmark_plot.png
28
29**Result**
30
31In many cases, when decoding H264 videos, using 2 threads give a good performance.
32
33.. image:: ../../_static/data/example-benchmark-video.png
34
35"""
36
37__all__ = [
38 "BenchmarkConfig",
39 "create_video_data",
40 "load_video_with_config",
41 "main",
42]
43
44import argparse
45import os
46import subprocess
47import tempfile
48from dataclasses import dataclass
49
50import spdl.io
51
52try:
53 from examples.benchmark_utils import ( # pyre-ignore[21]
54 BenchmarkResult,
55 BenchmarkRunner,
56 ExecutorType,
57 get_default_result_path,
58 save_results_to_csv,
59 )
60except ImportError:
61 from spdl.examples.benchmark_utils import (
62 BenchmarkResult,
63 BenchmarkRunner,
64 ExecutorType,
65 get_default_result_path,
66 save_results_to_csv,
67 )
68
69
70DEFAULT_RESULT_PATH: str = get_default_result_path(__file__)
71
72
73@dataclass(frozen=True)
74class BenchmarkConfig:
75 """BenchmarkConfig()
76
77 Configuration for a single video decoding benchmark run."""
78
79 resolution: str
80 """Video resolution label (e.g., "SD", "HD", "4K")"""
81
82 width: int
83 """Video width in pixels"""
84
85 height: int
86 """Video height in pixels"""
87
88 duration_seconds: float
89 """Duration of the video in seconds"""
90
91 num_workers: int
92 """Number of concurrent worker threads"""
93
94 decoder_threads: int
95 """Number of FFmpeg decoder threads"""
96
97 iterations: int
98 """Number of iterations per run"""
99
100 num_runs: int
101 """Number of runs for statistical analysis"""
102
103
104def create_video_data(
105 width: int = 1920,
106 height: int = 1080,
107 duration_seconds: float = 5.0,
108 fps: int = 30,
109) -> bytes:
110 """Create a mock H.264 video file in memory for benchmarking.
111
112 Args:
113 width: Video width in pixels
114 height: Video height in pixels
115 duration_seconds: Duration of video in seconds
116 fps: Frames per second
117
118 Returns:
119 Video file as bytes (H.264 encoded in MP4 container)
120 """
121 with tempfile.NamedTemporaryFile(suffix=".mp4") as tmp_file:
122 output_path = tmp_file.name
123
124 cmd = [
125 "ffmpeg",
126 "-f",
127 "lavfi",
128 "-i",
129 f"testsrc=duration={duration_seconds}:size={width}x{height}:rate={fps}",
130 "-c:v",
131 "libx264",
132 "-preset",
133 "ultrafast",
134 "-pix_fmt",
135 "yuv420p",
136 "-y",
137 output_path,
138 ]
139
140 subprocess.run(
141 cmd,
142 check=True,
143 stdout=subprocess.DEVNULL,
144 stderr=subprocess.DEVNULL,
145 )
146
147 with open(output_path, "rb") as f:
148 video_data = f.read()
149
150 return video_data
151
152
153def load_video_with_config(
154 video_data: bytes, decoder_threads: int
155) -> spdl.io.CPUBuffer:
156 """Load video data using spdl.io.load_video with specified decoder threads.
157
158 Args:
159 video_data: Video file data as bytes
160 decoder_threads: Number of threads for FFmpeg decoder
161
162 Returns:
163 Decoded video frames as CPUBuffer
164 """
165 decode_config = spdl.io.decode_config(
166 decoder_options={"threads": str(decoder_threads)}
167 )
168 return spdl.io.load_video(video_data, decode_config=decode_config)
169
170
171def _parse_args() -> argparse.Namespace:
172 """Parse command line arguments for the benchmark script.
173
174 Returns:
175 Parsed command line arguments
176 """
177 parser = argparse.ArgumentParser(description="Benchmark video decoding performance")
178 parser.add_argument(
179 "--output",
180 type=lambda p: os.path.realpath(p),
181 default=DEFAULT_RESULT_PATH,
182 help="Output file path.",
183 )
184 return parser.parse_args()
185
186
187def main() -> None:
188 """Run comprehensive benchmark suite for video decoding performance.
189
190 Benchmarks video decoding across different resolutions (SD, HD, 4K),
191 worker thread counts (1, 2, 4, 8), and decoder thread counts (1, 2, 4).
192 """
193 args = _parse_args()
194
195 video_configs = [
196 ("SD", 640, 480, 5.0),
197 ("HD", 1920, 1080, 5.0),
198 ("4K", 3840, 2160, 5.0),
199 ]
200
201 worker_counts = [1, 2, 4, 8]
202 decoder_thread_counts = [1, 2, 4]
203
204 results: list[BenchmarkResult[BenchmarkConfig]] = []
205
206 for resolution, width, height, duration in video_configs:
207 print(f"\nCreating {resolution} video ({width}x{height}, {duration}s)...")
208 video_data = create_video_data(
209 width=width, height=height, duration_seconds=duration
210 )
211 print(f"Video size: {len(video_data) / 1024 / 1024:.2f} MB")
212
213 print(f"\n{resolution} ({width}x{height})")
214 print("Workers,Decoder Threads,QPS,CI Lower,CI Upper,CPU %")
215
216 for num_workers in worker_counts:
217 with BenchmarkRunner(
218 executor_type=ExecutorType.THREAD,
219 num_workers=num_workers,
220 ) as runner:
221 for decoder_threads in decoder_thread_counts:
222 config = BenchmarkConfig(
223 resolution=resolution,
224 width=width,
225 height=height,
226 duration_seconds=duration,
227 num_workers=num_workers,
228 decoder_threads=decoder_threads,
229 iterations=num_workers * 2,
230 num_runs=5,
231 )
232
233 result, output = runner.run(
234 config,
235 lambda data=video_data,
236 threads=decoder_threads: load_video_with_config(data, threads),
237 config.iterations,
238 num_runs=config.num_runs,
239 )
240
241 results.append(result)
242
243 print(
244 f"{num_workers},{decoder_threads},"
245 f"{result.qps:.2f},{result.ci_lower:.2f},{result.ci_upper:.2f},"
246 f"{result.cpu_percent:.1f}"
247 )
248
249 save_results_to_csv(results, args.output)
250 print(
251 f"\nBenchmark complete. To generate plots, run:\n"
252 f"python benchmark_video_plot.py --input {args.output} "
253 f"--output {args.output.replace('.csv', '.png')}"
254 )
255
256
257if __name__ == "__main__":
258 main()
API Reference¶
Functions
- create_video_data(width: int = 1920, height: int = 1080, duration_seconds: float = 5.0, fps: int = 30) bytes[source]¶
Create a mock H.264 video file in memory for benchmarking.
- Parameters:
width – Video width in pixels
height – Video height in pixels
duration_seconds – Duration of video in seconds
fps – Frames per second
- Returns:
Video file as bytes (H.264 encoded in MP4 container)
- load_video_with_config(video_data: bytes, decoder_threads: int) CPUBuffer[source]¶
Load video data using spdl.io.load_video with specified decoder threads.
- Parameters:
video_data – Video file data as bytes
decoder_threads – Number of threads for FFmpeg decoder
- Returns:
Decoded video frames as CPUBuffer
- main() None[source]¶
Run comprehensive benchmark suite for video decoding performance.
Benchmarks video decoding across different resolutions (SD, HD, 4K), worker thread counts (1, 2, 4, 8), and decoder thread counts (1, 2, 4).
Classes