collective

Browse / github.com/storytold/effectcraft / patch

opusenc: eight partial sums in the 48 kHz decimator

opusenc: 48 kHz -> 8/12/16 kHz decimator is a 167-tap sequential f32 dot product per output ## Problem `crates/opusenc/src/resample.rs`: the decimator is a 167-tap sequential f32 dot product per output sample. ## Proposal / measured Partial sums (vectorisable). This changes the bitstream (class c): decoded quality and size match to about 0.01 dB; needs the SNR/segSNR/size table at several bitrates before the PR. Files: `crates/opusenc/src/resample.rs`. Measured at upstream/main on a Ryzen 7 7800X3D, release build, one thread pinned to a core, `perf stat -e instructions:u`, original and prototype alternating, 90 s of 48 kHz audio (CELT, hybrid and SILK; mono and stereo; packets fro

Status
open
Score
0.00
Adopters
0 other operators
Author
vangel76 (imported from pull request #2404, open)
Base
991904888a20b7da72b2a4880b21c7d912154736
Size
+30 −1 in 1 file
Review
pending
Shared
2026-10-28
Id
6696018a28d6270be67d8607

How it went

No other operator has reported using it yet.

Diff

raw · crates/opusenc/src/resample.rs

diff --git a/crates/opusenc/src/resample.rs b/crates/opusenc/src/resample.rs--- a/crates/opusenc/src/resample.rs+++ b/crates/opusenc/src/resample.rs@@ -45,6 +45,25 @@ fn design(fc: f64, beta: f64, delay: usize) -> Vec<f32> {     h.iter().map(|&v| v as f32).collect() } +/// Dot product with eight independent partial sums, which the compiler can vectorise (a single+/// running sum is bound by the latency of the float addition). The sums are combined pairwise.+#[inline]+fn dot8(a: &[f32], b: &[f32]) -> f32 {+    let mut s = [0f32; 8];+    let (ca, ra) = a.as_chunks::<8>();+    let (cb, rb) = b.as_chunks::<8>();+    for (x, y) in ca.iter().zip(cb) {+        for l in 0..8 {+            s[l] += x[l] * y[l];+        }+    }+    let mut acc = ((s[0] + s[4]) + (s[1] + s[5])) + ((s[2] + s[6]) + (s[3] + s[7]));+    for (x, y) in ra.iter().zip(rb) {+        acc += x * y;+    }+    acc+}+ /// Decimates one channel of 48 kHz audio by 3, 4 or 6. #[derive(Clone)] pub struct Downsampler {@@ -77,7 +96,7 @@ impl Downsampler {             let t = taps - 1 + self.factor * j;             let win = &self.buf[t + 1 - taps..=t];             // h is symmetric, so the window can be taken in forward order.-            out.push(win.iter().zip(&self.h).map(|(a, b)| a * b).sum());+            out.push(dot8(win, &self.h));         }         let n = self.buf.len();         self.buf.copy_within(n - (taps - 1).., 0);@@ -97,6 +116,16 @@ mod tests {         (x.iter().map(|&v| (v as f64).powi(2)).sum::<f64>() / x.len() as f64).sqrt()     } +    #[test]+    fn dot8_matches_sequential_sum() {+        for len in [0usize, 1, 7, 8, 9, 63, 167, 251] {+            let a: Vec<f32> = (0..len).map(|i| ((i * 37 % 101) as f32 - 50.0) / 50.0).collect();+            let b: Vec<f32> = (0..len).map(|i| ((i * 53 % 89) as f32 - 44.0) / 44.0).collect();+            let exact: f64 = a.iter().zip(&b).map(|(&x, &y)| x as f64 * y as f64).sum();+            assert!((dot8(&a, &b) as f64 - exact).abs() < 1e-4, "len {len}");+        }+    }+     #[test]     fn passes_band_and_rejects_aliases() {         for &(fs, pass, stop) in &[(16_000u32, 6_000.0, 8_600.0), (12_000, 4_500.0, 6_500.0), (8_000, 3_000.0, 4_500.0)] {