Benchmarks
PVIO is benchmarked against PyAV, OpenCV, Decord, TorchCodec, and NVIDIA DALI
across three tasks (plus a dedicated high-bit-depth/alpha decode check).
Results below are generated by
benchmark/run_all.py
and embedded here automatically at docs-build time. To regenerate locally:
uv run python -m benchmark.run_all
properdocs gh-deploy
See benchmark/README.md
for full methodology, metric definitions, and backend notes.
Bars are colored blue for CPU backends and pink/magenta for GPU backends
throughout; PVIO's own backends are the dark shade in each family, other
libraries the light shade.
Encode throughput and compression ratio vs quality
Encode at matched image quality
Encoders are compared at a common target PSNR rather than a shared CRF/QP
number, since those aren't equivalent quality knobs across encoders — see the
exact target and each encoder's achieved PSNR in the table below.
Decode throughput
High-bit-depth (FFV1 16-bit RGBA) decode throughput
Only backends verified to decode this format correctly are shown — most
libraries silently corrupt 16-bit/alpha content instead of erroring. See the
"Skipped / errored" table below and benchmark/README.md for specifics.
Benchmark result tables
Encoding at matched PSNR (frames/s and compression at equal image quality, interpolated from the sweep; x_psnr_matched=False means the encoder's sweep did not reach the target and the nearest point is shown)
| workload |
backend |
x_target_psnr |
x_psnr_db |
x_psnr_matched |
metric_main |
x_compression_ratio |
x_file_size_mb |
| enc_hd |
opencv |
34.74 |
36.1 |
False |
253.2 |
7.4 |
3.32 |
| enc_hd |
pvio_cpu |
34.74 |
34.73 |
True |
82.3 |
15.0 |
1.65 |
| enc_hd |
pvio_gpu |
34.74 |
34.73 |
True |
137.4 |
40.3 |
0.61 |
| enc_hd |
pyav |
34.74 |
34.73 |
True |
43.0 |
5.1 |
4.81 |
| enc_sd |
opencv |
34.74 |
36.08 |
False |
529.4 |
7.2 |
1.53 |
| enc_sd |
pvio_cpu |
34.74 |
34.74 |
True |
151.2 |
14.6 |
0.75 |
| enc_sd |
pvio_gpu |
34.74 |
34.74 |
True |
205.9 |
38.2 |
0.29 |
| enc_sd |
pyav |
34.74 |
34.74 |
True |
83.1 |
5.9 |
1.83 |
Encoding Pareto sweep (throughput vs compression per quality level)
| workload |
backend |
x_quality_param |
metric_main |
x_compression_ratio |
x_psnr_db |
| enc_hd |
opencv |
51.0 |
255.582 |
7.4 |
36.1 |
| enc_hd |
opencv |
55.0 |
250.409 |
7.4 |
36.1 |
| enc_hd |
opencv |
59.0 |
255.016 |
7.4 |
36.1 |
| enc_hd |
opencv |
63.0 |
256.065 |
7.4 |
36.1 |
| enc_hd |
opencv |
67.0 |
253.247 |
7.4 |
36.1 |
| enc_hd |
pvio_cpu |
17.0 |
39.02 |
2.1 |
35.73 |
| enc_hd |
pvio_cpu |
19.0 |
51.717 |
4.4 |
35.26 |
| enc_hd |
pvio_cpu |
21.0 |
69.962 |
9.7 |
35.01 |
| enc_hd |
pvio_cpu |
23.0 |
91.839 |
20.0 |
34.55 |
| enc_hd |
pvio_cpu |
25.0 |
133.364 |
48.8 |
34.1 |
| enc_hd |
pvio_gpu |
17.0 |
138.641 |
5.9 |
35.6 |
| enc_hd |
pvio_gpu |
19.0 |
138.818 |
13.2 |
35.36 |
| enc_hd |
pvio_gpu |
21.0 |
139.128 |
34.1 |
34.93 |
| enc_hd |
pvio_gpu |
23.0 |
135.676 |
48.1 |
34.53 |
| enc_hd |
pvio_gpu |
25.0 |
135.986 |
71.8 |
34.25 |
| enc_hd |
pyav |
17.0 |
30.682 |
2.1 |
35.22 |
| enc_hd |
pyav |
19.0 |
40.635 |
4.4 |
34.79 |
| enc_hd |
pyav |
21.0 |
54.302 |
9.7 |
34.51 |
| enc_hd |
pyav |
23.0 |
72.483 |
20.1 |
34.16 |
| enc_hd |
pyav |
25.0 |
101.837 |
49.0 |
33.96 |
| enc_sd |
opencv |
51.0 |
564.329 |
7.2 |
36.08 |
| enc_sd |
opencv |
55.0 |
553.27 |
7.2 |
36.08 |
| enc_sd |
opencv |
59.0 |
571.265 |
7.2 |
36.08 |
| enc_sd |
opencv |
63.0 |
563.474 |
7.2 |
36.08 |
| enc_sd |
opencv |
67.0 |
529.367 |
7.2 |
36.08 |
| enc_sd |
pvio_cpu |
17.0 |
74.063 |
2.1 |
35.71 |
| enc_sd |
pvio_cpu |
19.0 |
96.36 |
4.5 |
35.26 |
| enc_sd |
pvio_cpu |
21.0 |
127.903 |
9.8 |
34.97 |
| enc_sd |
pvio_cpu |
23.0 |
173.577 |
20.2 |
34.55 |
| enc_sd |
pvio_cpu |
25.0 |
247.799 |
50.2 |
34.21 |
| enc_sd |
pvio_gpu |
17.0 |
208.604 |
5.9 |
35.59 |
| enc_sd |
pvio_gpu |
19.0 |
203.813 |
13.2 |
35.35 |
| enc_sd |
pvio_gpu |
21.0 |
206.047 |
33.0 |
34.91 |
| enc_sd |
pvio_gpu |
23.0 |
205.619 |
45.8 |
34.53 |
| enc_sd |
pvio_gpu |
25.0 |
203.721 |
66.8 |
34.21 |
| enc_sd |
pyav |
17.0 |
56.326 |
2.1 |
35.27 |
| enc_sd |
pyav |
19.0 |
72.967 |
4.4 |
34.85 |
| enc_sd |
pyav |
21.0 |
102.834 |
9.7 |
34.56 |
| enc_sd |
pyav |
23.0 |
135.795 |
20.2 |
34.25 |
| enc_sd |
pyav |
25.0 |
193.97 |
50.0 |
34.08 |
Random access (frames/s, higher better)
| workload |
opencv_cpu |
pvio_io_buffered |
pvio_video_cpu |
pvio_video_gpu |
pyav_cpu |
torchcodec_cpu |
torchcodec_cuda |
| fhd_h264 |
3.0 |
37.0 |
23.0 |
128.0 |
6.0 |
38.0 |
149.0 |
| hd_h264 |
8.0 |
86.0 |
52.0 |
264.0 |
13.0 |
90.0 |
310.0 |
| sd_h264 |
17.0 |
184.0 |
133.0 |
459.0 |
30.0 |
192.0 |
547.0 |
Seek correctness (all videos)
| backend |
x_seek_correct |
| opencv_cpu |
True |
| pvio_io_buffered |
True |
| pvio_video_cpu |
True |
| pvio_video_gpu |
True |
| pyav_cpu |
True |
| torchcodec_cpu |
True |
| torchcodec_cuda |
True |
Sequential access (frames/s, higher better)
| workload |
opencv_cpu |
pvio_io_buffered |
pvio_io_stream |
pvio_video_gpu |
pyav_cpu |
torchcodec_cpu |
torchcodec_cuda |
| fhd_h264 |
476.0 |
369.0 |
515.0 |
543.0 |
613.0 |
505.0 |
547.0 |
| hd_h264 |
1148.0 |
845.0 |
1148.0 |
1117.0 |
1356.0 |
1131.0 |
1118.0 |
| sd_h264 |
2203.0 |
1738.0 |
2303.0 |
1789.0 |
2715.0 |
2201.0 |
2041.0 |
High-bit-depth sequential access — FFV1 16-bit RGBA (frames/s, higher better; most libraries are excluded rather than measured, see 'Skipped / errored' below)
| workload |
pvio_io_buffered |
pvio_io_stream |
pyav_cpu |
| sd_ffv1_rgba16 |
237.0 |
252.0 |
269.0 |
High-bit-depth decode correctness (dtype/shape/values exact)
| backend |
x_value_correct |
| pvio_io_buffered |
True |
| pvio_io_stream |
True |
| pyav_cpu |
True |
Skipped / errored
| task |
backend |
workload |
error |
| random |
decord_cpu |
sd_h264 |
import failed: No module named 'decord' |
| random |
decord_cpu |
hd_h264 |
import failed: No module named 'decord' |
| random |
decord_cpu |
fhd_h264 |
import failed: No module named 'decord' |
| sequential |
dali_gpu |
sd_h264 |
ImportError: cannot import name 'fn' from 'nvidia.dali' (unknown location) |
| sequential |
decord_cpu |
sd_h264 |
import failed: No module named 'decord' |
| sequential |
dali_gpu |
hd_h264 |
ImportError: cannot import name 'fn' from 'nvidia.dali' (unknown location) |
| sequential |
decord_cpu |
hd_h264 |
import failed: No module named 'decord' |
| sequential |
dali_gpu |
fhd_h264 |
ImportError: cannot import name 'fn' from 'nvidia.dali' (unknown location) |
| sequential |
decord_cpu |
fhd_h264 |
import failed: No module named 'decord' |
| sequential_hdr |
dali_gpu |
sd_ffv1_rgba16 |
no verified FFV1/16-bit-RGBA decode support |
| sequential_hdr |
decord_cpu |
sd_ffv1_rgba16 |
no verified FFV1/16-bit-RGBA decode support |
| sequential_hdr |
opencv_cpu |
sd_ffv1_rgba16 |
OpenCV's default BGR conversion collapses FFV1 gbrap16le the same way (verified: reads back as uint8, every value misread as 1); excluded rather than reported as if correct |
| sequential_hdr |
pvio_video_gpu |
sd_ffv1_rgba16 |
TorchCodec silently downconverts FFV1 gbrap16le to corrupted 8-bit and drops alpha (verified: decodes to uint8 with every value misread as 1) rather than erroring; excluded rather than reported as if correct |
| sequential_hdr |
torchcodec_cpu |
sd_ffv1_rgba16 |
TorchCodec silently downconverts FFV1 gbrap16le to corrupted 8-bit and drops alpha (verified: decodes to uint8 with every value misread as 1) rather than erroring; excluded rather than reported as if correct |
| sequential_hdr |
torchcodec_cuda |
sd_ffv1_rgba16 |
TorchCodec silently downconverts FFV1 gbrap16le to corrupted 8-bit and drops alpha (verified: decodes to uint8 with every value misread as 1) rather than erroring; excluded rather than reported as if correct |