Skip to content

Commit b1d7bfe

Browse files
committed
Port filter to Rust
This is about 2x slower than the C implementation but completely unoptimized so far. With this the last component is ported to C and only porting the API layer and input chunking is remaining.
1 parent 50347db commit b1d7bfe

File tree

8 files changed

+1335
-294
lines changed

8 files changed

+1335
-294
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ harness = false
4141
name = "history"
4242
harness = false
4343

44+
[[bench]]
45+
name = "filter"
46+
harness = false
47+
4448
[[bench]]
4549
name = "ebur128"
4650
harness = false

benches/filter.rs

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
2+
3+
// Run all benchmarks without true peak calculation as that's just another function call
4+
// and we measure that one in the true peak benchmarks already.
5+
6+
#[cfg(feature = "internal-tests")]
7+
fn filter_i16_c(rate: u32, channels: u32, src: &[i16], dest: &mut [f64], channel_map: &[u32]) {
8+
use ebur128::filter;
9+
10+
unsafe {
11+
let f = filter::filter_create_c(rate, channels, 1, 0);
12+
filter::filter_process_short_c(
13+
f,
14+
src.len() / channels as usize,
15+
src.as_ptr(),
16+
dest.as_mut_ptr(),
17+
channel_map.as_ptr(),
18+
);
19+
filter::filter_destroy_c(f);
20+
}
21+
}
22+
23+
#[cfg(feature = "internal-tests")]
24+
fn filter_i16(rate: u32, channels: u32, src: &[i16], dest: &mut [f64], channel_map: &[u32]) {
25+
use ebur128::filter;
26+
27+
let mut f = filter::Filter::new(rate, channels, true, false);
28+
f.process(src, dest, channel_map);
29+
}
30+
31+
#[cfg(feature = "internal-tests")]
32+
fn filter_i32_c(rate: u32, channels: u32, src: &[i32], dest: &mut [f64], channel_map: &[u32]) {
33+
use ebur128::filter;
34+
35+
unsafe {
36+
let f = filter::filter_create_c(rate, channels, 1, 0);
37+
filter::filter_process_int_c(
38+
f,
39+
src.len() / channels as usize,
40+
src.as_ptr(),
41+
dest.as_mut_ptr(),
42+
channel_map.as_ptr(),
43+
);
44+
filter::filter_destroy_c(f);
45+
}
46+
}
47+
48+
#[cfg(feature = "internal-tests")]
49+
fn filter_i32(rate: u32, channels: u32, src: &[i32], dest: &mut [f64], channel_map: &[u32]) {
50+
use ebur128::filter;
51+
52+
let mut f = filter::Filter::new(rate, channels, true, false);
53+
f.process(src, dest, channel_map);
54+
}
55+
56+
#[cfg(feature = "internal-tests")]
57+
fn filter_f32_c(rate: u32, channels: u32, src: &[f32], dest: &mut [f64], channel_map: &[u32]) {
58+
use ebur128::filter;
59+
60+
unsafe {
61+
let f = filter::filter_create_c(rate, channels, 1, 0);
62+
filter::filter_process_float_c(
63+
f,
64+
src.len() / channels as usize,
65+
src.as_ptr(),
66+
dest.as_mut_ptr(),
67+
channel_map.as_ptr(),
68+
);
69+
filter::filter_destroy_c(f);
70+
}
71+
}
72+
73+
#[cfg(feature = "internal-tests")]
74+
fn filter_f32(rate: u32, channels: u32, src: &[f32], dest: &mut [f64], channel_map: &[u32]) {
75+
use ebur128::filter;
76+
77+
let mut f = filter::Filter::new(rate, channels, true, false);
78+
f.process(src, dest, channel_map);
79+
}
80+
81+
#[cfg(feature = "internal-tests")]
82+
fn filter_f64_c(rate: u32, channels: u32, src: &[f64], dest: &mut [f64], channel_map: &[u32]) {
83+
use ebur128::filter;
84+
85+
unsafe {
86+
let f = filter::filter_create_c(rate, channels, 1, 0);
87+
filter::filter_process_double_c(
88+
f,
89+
src.len() / channels as usize,
90+
src.as_ptr(),
91+
dest.as_mut_ptr(),
92+
channel_map.as_ptr(),
93+
);
94+
filter::filter_destroy_c(f);
95+
}
96+
}
97+
98+
#[cfg(feature = "internal-tests")]
99+
fn filter_f64(rate: u32, channels: u32, src: &[f64], dest: &mut [f64], channel_map: &[u32]) {
100+
use ebur128::filter;
101+
102+
let mut f = filter::Filter::new(rate, channels, true, false);
103+
f.process(src, dest, channel_map);
104+
}
105+
106+
pub fn criterion_benchmark(c: &mut Criterion) {
107+
#[cfg(feature = "internal-tests")]
108+
{
109+
let channel_map = [1; 2];
110+
let mut data_out = vec![0.0f64; 19200 * 2];
111+
let mut data = vec![0i16; 19200 * 2];
112+
let mut accumulator = 0.0;
113+
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
114+
for out in data.chunks_exact_mut(2) {
115+
let val = f32::sin(accumulator) * std::i16::MAX as f32;
116+
out[0] = val as i16;
117+
out[1] = val as i16;
118+
accumulator += step;
119+
}
120+
121+
let mut group = c.benchmark_group("filter: 48kHz 2ch i16");
122+
123+
group.bench_function("C", |b| {
124+
b.iter(|| {
125+
filter_i16_c(
126+
black_box(48_000),
127+
black_box(2),
128+
black_box(&data),
129+
black_box(&mut data_out),
130+
black_box(&channel_map),
131+
)
132+
})
133+
});
134+
135+
group.bench_function("Rust", |b| {
136+
b.iter(|| {
137+
filter_i16(
138+
black_box(48_000),
139+
black_box(2),
140+
black_box(&data),
141+
black_box(&mut data_out),
142+
black_box(&channel_map),
143+
)
144+
})
145+
});
146+
147+
group.finish();
148+
149+
let mut data = vec![0i32; 19200 * 2];
150+
let mut accumulator = 0.0;
151+
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
152+
for out in data.chunks_exact_mut(2) {
153+
let val = f32::sin(accumulator) * std::i32::MAX as f32;
154+
out[0] = val as i32;
155+
out[1] = val as i32;
156+
accumulator += step;
157+
}
158+
159+
let mut group = c.benchmark_group("filter: 48kHz 2ch i32");
160+
161+
group.bench_function("C", |b| {
162+
b.iter(|| {
163+
filter_i32_c(
164+
black_box(48_000),
165+
black_box(2),
166+
black_box(&data),
167+
black_box(&mut data_out),
168+
black_box(&channel_map),
169+
)
170+
})
171+
});
172+
173+
group.bench_function("Rust", |b| {
174+
b.iter(|| {
175+
filter_i32(
176+
black_box(48_000),
177+
black_box(2),
178+
black_box(&data),
179+
black_box(&mut data_out),
180+
black_box(&channel_map),
181+
)
182+
})
183+
});
184+
185+
group.finish();
186+
187+
let mut data = vec![0.0f32; 19200 * 2];
188+
let mut accumulator = 0.0;
189+
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
190+
for out in data.chunks_exact_mut(2) {
191+
let val = f32::sin(accumulator);
192+
out[0] = val;
193+
out[1] = val;
194+
accumulator += step;
195+
}
196+
197+
let mut group = c.benchmark_group("filter: 48kHz 2ch f32");
198+
199+
group.bench_function("C", |b| {
200+
b.iter(|| {
201+
filter_f32_c(
202+
black_box(48_000),
203+
black_box(2),
204+
black_box(&data),
205+
black_box(&mut data_out),
206+
black_box(&channel_map),
207+
)
208+
})
209+
});
210+
211+
group.bench_function("Rust", |b| {
212+
b.iter(|| {
213+
filter_f32(
214+
black_box(48_000),
215+
black_box(2),
216+
black_box(&data),
217+
black_box(&mut data_out),
218+
black_box(&channel_map),
219+
)
220+
})
221+
});
222+
223+
group.finish();
224+
225+
let mut data = vec![0.0f64; 19200 * 2];
226+
let mut accumulator = 0.0;
227+
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
228+
for out in data.chunks_exact_mut(2) {
229+
let val = f32::sin(accumulator);
230+
out[0] = val as f64;
231+
out[1] = val as f64;
232+
accumulator += step;
233+
}
234+
235+
let mut group = c.benchmark_group("filter: 48kHz 2ch f64");
236+
237+
group.bench_function("C", |b| {
238+
b.iter(|| {
239+
filter_f64_c(
240+
black_box(48_000),
241+
black_box(2),
242+
black_box(&data),
243+
black_box(&mut data_out),
244+
black_box(&channel_map),
245+
)
246+
})
247+
});
248+
249+
group.bench_function("Rust", |b| {
250+
b.iter(|| {
251+
filter_f64(
252+
black_box(48_000),
253+
black_box(2),
254+
black_box(&data),
255+
black_box(&mut data_out),
256+
black_box(&channel_map),
257+
)
258+
})
259+
});
260+
261+
group.finish();
262+
}
263+
}
264+
265+
criterion_group!(benches, criterion_benchmark);
266+
criterion_main!(benches);

build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ fn main() {
77
b.file("src/c/tests/interp.c");
88
b.file("src/c/tests/true_peak.c");
99
b.file("src/c/tests/history.c");
10+
b.file("src/c/tests/filter.c");
1011
}
1112

1213
b.file("src/c/ebur128.c");

0 commit comments

Comments
 (0)