Repository navigation
Expand file tree
/
Copy pathperf.rs
More file actions
113 lines (106 loc) · 4.26 KB
/
Copy pathperf.rs
File metadata and controls
113 lines (106 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! Decode throughput and per-stage breakdown (ignored; run with
//! `cargo test --release -p filmcraft-av1 --test perf -- --ignored --nocapture`).
//!
//! The machine may be busy (other builds), so single-threaded work is also measured with the
//! thread CPU clock, which is robust to load; wall-clock figures are printed next to the load
//! average.
mod common;
use common::*;
use filmcraft_av1::{DecodeStats, Decoder, Stage};
use std::time::Instant;
fn thread_cpu_secs() -> Option<f64> {
#[cfg(unix)]
{
let t = rustix::time::clock_gettime(rustix::time::ClockId::ThreadCPUTime);
Some(t.tv_sec as f64 + t.tv_nsec as f64 * 1e-9)
}
#[cfg(not(unix))]
{
None
}
}
fn load_average() -> String {
let out = std::process::Command::new("sysctl").args(["-n", "vm.loadavg"]).output().ok();
if let Some(o) = out.filter(|o| o.status.success()) {
return String::from_utf8_lossy(&o.stdout).trim().trim_matches(|c| c == '{' || c == '}').trim().to_string();
}
std::fs::read_to_string("/proc/loadavg").map(|s| s.split_whitespace().take(3).collect::<Vec<_>>().join(" ")).unwrap_or_else(|_| "?".into())
}
struct Run {
wall: f64,
cpu: Option<f64>,
shown: usize,
stats: DecodeStats,
}
fn run(frames: &[Vec<u8>], threads: usize) -> Run {
let mut dec = Decoder::with_threads(threads);
let t = Instant::now();
let c0 = thread_cpu_secs();
let mut shown = 0;
for f in frames {
shown += dec.decode(f).unwrap().len();
}
shown += dec.flush().len();
let cpu = c0.and_then(|c0| thread_cpu_secs().map(|c1| c1 - c0));
Run { wall: t.elapsed().as_secs_f64(), cpu, shown, stats: dec.stats() }
}
fn bench(spec: &Spec) {
// AV1_PERF_ONLY=<substring> limits the run to matching fixtures.
if std::env::var("AV1_PERF_ONLY").is_ok_and(|f| !spec.name.contains(f.as_str())) {
return;
}
let Some(ff) = ffmpeg() else { return };
let path = make(&ff, spec);
let frames = ivf_frames(&path);
// Single-threaded: best of 3 by thread CPU time (falls back to wall time).
let mut best: Option<Run> = None;
for _ in 0..3 {
let r = run(&frames, 1);
let key = |r: &Run| r.cpu.unwrap_or(r.wall);
if best.as_ref().is_none_or(|b| key(&r) < key(b)) {
best = Some(r);
}
}
let b = best.unwrap();
let n = b.stats.frames.max(1) as f64;
let cpu_fps = b.cpu.map(|c| format!("{:6.1}", b.shown as f64 / c)).unwrap_or_else(|| " ?".into());
println!(
"{:<26} {:>3} shown {:>3} decoded {:>3} tiles/frame | 1 thread: {} fps (thread CPU) {:6.1} fps (wall)",
spec.name,
b.shown,
b.stats.frames,
b.stats.tiles / b.stats.frames.max(1),
cpu_fps,
b.shown as f64 / b.wall
);
let total = b.stats.total_secs();
let mut line = String::from(" ms/frame:");
for s in Stage::ALL {
let v = b.stats.secs(s);
if v > 0.0005 {
line += &format!(" {} {:.2} ({:.0}%)", s.name(), 1e3 * v / n, 100.0 * v / total);
}
}
println!("{line}");
// All cores: best of 3 by wall time.
let threads = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
let mut best_wall = f64::INFINITY;
for _ in 0..3 {
let r = run(&frames, threads);
assert_eq!(r.shown, b.shown);
best_wall = best_wall.min(r.wall);
}
println!(" {threads} threads: {:6.1} fps (wall, load average {})", b.shown as f64 / best_wall, load_average());
}
#[test]
#[ignore]
fn decode_throughput() {
let p = |crf: &'static str, params: &'static str| -> Vec<&'static str> { vec!["-preset", "8", "-crf", crf, "-svtav1-params", params] };
let src = "testsrc2=s=1920x1080:r=25,noise=alls=6:allf=t";
bench(&Spec::new("perf_1080p_intra", src, 10, "yuv420p", &p("30", "keyint=1")));
bench(&Spec::new("perf_1080p_gop", src, 60, "yuv420p", &p("30", "keyint=60")));
bench(&Spec::new("perf_1080p_gop_10bit", src, 60, "yuv420p10le", &p("30", "keyint=60")));
// Higher quality (more coefficients, finer partitions) and a multi-tile stream (4x2 tiles).
bench(&Spec::new("perf_1080p_gop_hq", src, 60, "yuv420p", &p("18", "keyint=60")));
bench(&Spec::new("perf_1080p_gop_tiles", src, 60, "yuv420p", &p("30", "keyint=60:tile-columns=2:tile-rows=1")));
}