collective

Browse / github.com/storytold/effectcraft / patch

vp9enc: forward transform accumulates all outputs together

vp9enc: forward transform has a serial f64 add chain and a libm floor per output (E3, -41 % of that stage) ## Proposal `crates/vp9enc/src/transform.rs`: accumulate the outputs together (no serial add chain per output) and truncate instead of calling `floor`. A separable 4-point float/integer forward transform would be about 2x fewer multiplies but changes rounding (separate decision). ## Measured (prototype) Forward transform 2.33 G -> 1.38 G instructions per 12 frames (-41 %). Output byte-identical. Files: `crates/vp9enc/src/transform.rs`. Measured at upstream/main on a Ryzen 7 7800X3D, release build, one thread pinned to a core, `perf stat -e instructions:u` as the primary number (the machine

Status
open
Score
0.00
Adopters
0 other operators
Author
vangel76 (imported from pull request #2446, open)
Base
966719d449f0fe5a81b19d1266e4a76ac90d6bca
Size
+24 −3 in 1 file
Review
pending
Shared
2026-10-28
Id
8d5d08c1397f3820456e447a

How it went

No other operator has reported using it yet.

Diff

raw · crates/vp9enc/src/transform.rs

diff --git a/crates/vp9enc/src/transform.rs b/crates/vp9enc/src/transform.rs--- a/crates/vp9enc/src/transform.rs+++ b/crates/vp9enc/src/transform.rs@@ -144,6 +144,16 @@ pub fn forward_matrices() -> &'static [[[f64; 16]; 16]; 4] {     }) } +/// [`forward_matrices`] transposed: `[type][q][k]`.+fn forward_matrices_t() -> &'static [[[f64; 16]; 16]; 4] {+    use std::sync::OnceLock;+    static M: OnceLock<[[[f64; 16]; 16]; 4]> = OnceLock::new();+    M.get_or_init(|| {+        let f = forward_matrices();+        std::array::from_fn(|t| std::array::from_fn(|q| std::array::from_fn(|k| f[t][k][q])))+    })+}+ fn invert16(m: [[f64; 16]; 16]) -> [[f64; 16]; 16] {     let mut a = m;     let mut inv = [[0.0; 16]; 16];@@ -191,12 +201,23 @@ impl Quant {         if self.lossless {             return forward_wht(res);         }-        let f = &forward_matrices()[tx_type as usize];+        let ft = &forward_matrices_t()[tx_type as usize];+        // Every output accumulates its 16 products in the same order (q ascending) as a+        // per-output sum, but the 16 outputs advance together: 16 independent chains that+        // vectorize instead of one serial chain per output.+        let mut acc = [-0.0f64; 16];+        for (q, col) in ft.iter().enumerate() {+            let r = res[q] as f64;+            for k in 0..16 {+                acc[k] += col[k] * r;+            }+        }         let mut lv = [0i32; 16];         for k in 0..16 {-            let coef: f64 = (0..16).map(|q| f[k][q] * res[q] as f64).sum();+            let coef = acc[k];             let (qs, rnd) = if k == 0 { (self.dcq, self.round_dc) } else { (self.acq, self.round_ac) };-            let l = (coef.abs() / qs as f64 + rnd).floor() as i32;+            // Non-negative: truncation is floor.+            let l = (coef.abs() / qs as f64 + rnd) as i32;             lv[k] = if coef < 0.0 { -l } else { l };         }         lv