Skip to content

Commit 852fdd7

Browse files
authored
Merge pull request #380 from orottier/feature/criterion
Run benchmarks locally with criterion
2 parents 6cfc2d7 + 08cd6ae commit 852fdd7

File tree

3 files changed

+129
-20
lines changed

3 files changed

+129
-20
lines changed

.github/workflows/benchmark.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ jobs:
5353
- name: Revert when benches do not compile on main
5454
run: cargo check --benches --no-default-features || git checkout main -- benches/my_benchmark.rs
5555
- name: Run benchmarks for main branch
56-
run: cargo bench --no-default-features
56+
run: cargo bench --no-default-features --feature iai
5757
- name: Checkout PR branch
5858
run: git checkout -
5959
- name: Run bench against baseline
60-
run: cargo bench --no-default-features | sed '0,/^test result:/d' | tee bench.txt
60+
run: cargo bench --no-default-features --feature iai | sed '0,/^test result:/d' | tee bench.txt
6161

6262
# for testing
6363
# - name: create mock results

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ vecmath = "1.0"
3535

3636
[dev-dependencies]
3737
alloc_counter = "0.0.4"
38+
criterion = "0.5.1"
3839
env_logger = "0.10"
3940
iai = "0.1.1"
4041
rand = "0.8"
@@ -62,3 +63,4 @@ cpal = ["dep:cpal"]
6263
cubeb = ["dep:cubeb"]
6364
cpal-jack = ["cpal", "cpal/jack"]
6465
cpal-asio = ["cpal", "cpal/asio"]
66+
iai = []

benches/my_benchmark.rs

Lines changed: 125 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,50 @@
1+
#[cfg(feature = "iai")]
12
use iai::black_box;
23

4+
#[cfg(not(feature = "iai"))]
5+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
6+
37
use web_audio_api::context::BaseAudioContext;
48
use web_audio_api::context::OfflineAudioContext;
59
use web_audio_api::node::{AudioNode, AudioScheduledSourceNode, PanningModelType};
10+
use web_audio_api::AudioBuffer;
11+
12+
use std::fs::File;
13+
use std::sync::OnceLock;
614

715
const SAMPLE_RATE: f32 = 48000.;
816
const DURATION: usize = 10;
917
const SAMPLES: usize = SAMPLE_RATE as usize * DURATION;
18+
const SAMPLES_SHORT: usize = SAMPLE_RATE as usize; // only 1 second for heavy benchmarks
19+
20+
/// Load an audio buffer and cache the result
21+
///
22+
/// We don't want to measure the IO and decoding in most of our benchmarks, so by using this static
23+
/// instance we avoid this in the criterion benchmarks because the file is already loaded in the
24+
/// warmup phase.
25+
fn get_audio_buffer(ctx: &OfflineAudioContext) -> AudioBuffer {
26+
static BUFFER: OnceLock<AudioBuffer> = OnceLock::new();
27+
BUFFER
28+
.get_or_init(|| {
29+
let file = File::open("samples/think-stereo-48000.wav").unwrap();
30+
ctx.decode_audio_data_sync(file).unwrap()
31+
})
32+
.clone()
33+
}
1034

1135
pub fn bench_ctor() {
1236
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
1337
assert_eq!(ctx.start_rendering_sync().length(), SAMPLES);
1438
}
1539

40+
// This benchmark only makes sense in `iai`, because subsequent runs use the cached audiobuffer.
41+
// However we would like to run this test here so the cache is filled for the subsequent benches.
42+
pub fn bench_audio_buffer_decode() {
43+
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
44+
let buffer = get_audio_buffer(&ctx);
45+
assert_eq!(buffer.length(), 101129);
46+
}
47+
1648
pub fn bench_sine() {
1749
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
1850
let mut osc = ctx.create_oscillator();
@@ -56,9 +88,7 @@ pub fn bench_sine_gain_delay() {
5688

5789
pub fn bench_buffer_src() {
5890
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
59-
60-
let file = std::fs::File::open("samples/think-stereo-48000.wav").unwrap();
61-
let buffer = ctx.decode_audio_data_sync(file).unwrap();
91+
let buffer = get_audio_buffer(&ctx);
6292

6393
let mut src = ctx.create_buffer_source();
6494
src.connect(&ctx.destination());
@@ -70,9 +100,7 @@ pub fn bench_buffer_src() {
70100

71101
pub fn bench_buffer_src_delay() {
72102
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
73-
74-
let file = std::fs::File::open("samples/think-stereo-48000.wav").unwrap();
75-
let buffer = ctx.decode_audio_data_sync(file).unwrap();
103+
let buffer = get_audio_buffer(&ctx);
76104

77105
let delay = ctx.create_delay(0.3);
78106
delay.delay_time().set_value(0.2);
@@ -89,8 +117,7 @@ pub fn bench_buffer_src_delay() {
89117

90118
pub fn bench_buffer_src_iir() {
91119
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
92-
let file = std::fs::File::open("samples/think-stereo-48000.wav").unwrap();
93-
let buffer = ctx.decode_audio_data_sync(file).unwrap();
120+
let buffer = get_audio_buffer(&ctx);
94121

95122
// these values correspond to a lowpass filter at 200Hz (calculated from biquad)
96123
let feedforward = vec![
@@ -116,8 +143,7 @@ pub fn bench_buffer_src_iir() {
116143

117144
pub fn bench_buffer_src_biquad() {
118145
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
119-
let file = std::fs::File::open("samples/think-stereo-48000.wav").unwrap();
120-
let buffer = ctx.decode_audio_data_sync(file).unwrap();
146+
let buffer = get_audio_buffer(&ctx);
121147

122148
// Create an biquad filter node (defaults to low pass)
123149
let biquad = ctx.create_biquad_filter();
@@ -135,8 +161,7 @@ pub fn bench_buffer_src_biquad() {
135161

136162
pub fn bench_stereo_positional() {
137163
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
138-
let file = std::fs::File::open("samples/think-stereo-48000.wav").unwrap();
139-
let buffer = ctx.decode_audio_data_sync(file).unwrap();
164+
let buffer = get_audio_buffer(&ctx);
140165

141166
// Create static panner node
142167
let panner = ctx.create_panner();
@@ -159,8 +184,7 @@ pub fn bench_stereo_positional() {
159184

160185
pub fn bench_stereo_panning_automation() {
161186
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
162-
let file = std::fs::File::open("samples/think-stereo-48000.wav").unwrap();
163-
let buffer = ctx.decode_audio_data_sync(file).unwrap();
187+
let buffer = get_audio_buffer(&ctx);
164188

165189
let panner = ctx.create_stereo_panner();
166190
panner.connect(&ctx.destination());
@@ -181,8 +205,7 @@ pub fn bench_stereo_panning_automation() {
181205
// benchmark this in deterministic way [citation needed].
182206
pub fn bench_analyser_node() {
183207
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
184-
let file = std::fs::File::open("samples/think-stereo-48000.wav").unwrap();
185-
let buffer = ctx.decode_audio_data_sync(file).unwrap();
208+
let buffer = get_audio_buffer(&ctx);
186209

187210
let analyser = ctx.create_analyser();
188211
analyser.connect(&ctx.destination());
@@ -197,7 +220,7 @@ pub fn bench_analyser_node() {
197220
}
198221

199222
pub fn bench_hrtf_panners() {
200-
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES), SAMPLE_RATE);
223+
let ctx = OfflineAudioContext::new(2, black_box(SAMPLES_SHORT), SAMPLE_RATE);
201224

202225
let mut panner1 = ctx.create_panner();
203226
panner1.set_panning_model(PanningModelType::HRTF);
@@ -214,11 +237,13 @@ pub fn bench_hrtf_panners() {
214237
osc.connect(&panner2);
215238
osc.start();
216239

217-
assert_eq!(ctx.start_rendering_sync().length(), SAMPLES);
240+
assert_eq!(ctx.start_rendering_sync().length(), SAMPLES_SHORT);
218241
}
219242

243+
#[cfg(feature = "iai")]
220244
iai::main!(
221245
bench_ctor,
246+
bench_audio_buffer_decode,
222247
bench_sine,
223248
bench_sine_gain,
224249
bench_sine_gain_delay,
@@ -231,3 +256,85 @@ iai::main!(
231256
bench_analyser_node,
232257
bench_hrtf_panners,
233258
);
259+
260+
#[cfg(not(feature = "iai"))]
261+
fn criterion_ctor(c: &mut Criterion) {
262+
c.bench_function("bench_ctor", |b| b.iter(bench_ctor));
263+
}
264+
#[cfg(not(feature = "iai"))]
265+
fn criterion_audio_buffer_decode(c: &mut Criterion) {
266+
c.bench_function("bench_audio_buffer_decode", |b| {
267+
b.iter(bench_audio_buffer_decode)
268+
});
269+
}
270+
#[cfg(not(feature = "iai"))]
271+
fn criterion_sine(c: &mut Criterion) {
272+
c.bench_function("bench_sine", |b| b.iter(bench_sine));
273+
}
274+
#[cfg(not(feature = "iai"))]
275+
fn criterion_sine_gain(c: &mut Criterion) {
276+
c.bench_function("bench_sine_gain", |b| b.iter(bench_sine_gain));
277+
}
278+
#[cfg(not(feature = "iai"))]
279+
fn criterion_sine_gain_delay(c: &mut Criterion) {
280+
c.bench_function("bench_sine_gain_delay", |b| b.iter(bench_sine_gain_delay));
281+
}
282+
#[cfg(not(feature = "iai"))]
283+
fn criterion_buffer_src(c: &mut Criterion) {
284+
c.bench_function("bench_buffer_src", |b| b.iter(bench_buffer_src));
285+
}
286+
#[cfg(not(feature = "iai"))]
287+
fn criterion_buffer_src_delay(c: &mut Criterion) {
288+
c.bench_function("bench_buffer_src_delay", |b| b.iter(bench_buffer_src_delay));
289+
}
290+
#[cfg(not(feature = "iai"))]
291+
fn criterion_buffer_src_iir(c: &mut Criterion) {
292+
c.bench_function("bench_buffer_src_iir", |b| b.iter(bench_buffer_src_iir));
293+
}
294+
#[cfg(not(feature = "iai"))]
295+
fn criterion_buffer_src_biquad(c: &mut Criterion) {
296+
c.bench_function("bench_buffer_src_biquad", |b| {
297+
b.iter(bench_buffer_src_biquad)
298+
});
299+
}
300+
#[cfg(not(feature = "iai"))]
301+
fn criterion_stereo_positional(c: &mut Criterion) {
302+
c.bench_function("bench_stereo_positional", |b| {
303+
b.iter(bench_stereo_positional)
304+
});
305+
}
306+
#[cfg(not(feature = "iai"))]
307+
fn criterion_stereo_panning_automation(c: &mut Criterion) {
308+
c.bench_function("bench_stereo_panning_automation", |b| {
309+
b.iter(bench_stereo_panning_automation)
310+
});
311+
}
312+
#[cfg(not(feature = "iai"))]
313+
fn criterion_analyser_node(c: &mut Criterion) {
314+
c.bench_function("bench_analyser_node", |b| b.iter(bench_analyser_node));
315+
}
316+
#[cfg(not(feature = "iai"))]
317+
fn criterion_hrtf_panners(c: &mut Criterion) {
318+
c.bench_function("bench_hrtf_panners", |b| b.iter(bench_hrtf_panners));
319+
}
320+
321+
#[cfg(not(feature = "iai"))]
322+
criterion_group!(
323+
benches,
324+
criterion_ctor,
325+
criterion_audio_buffer_decode,
326+
criterion_sine,
327+
criterion_sine_gain,
328+
criterion_sine_gain_delay,
329+
criterion_buffer_src,
330+
criterion_buffer_src_delay,
331+
criterion_buffer_src_iir,
332+
criterion_buffer_src_biquad,
333+
criterion_stereo_positional,
334+
criterion_stereo_panning_automation,
335+
criterion_analyser_node,
336+
criterion_hrtf_panners
337+
);
338+
339+
#[cfg(not(feature = "iai"))]
340+
criterion_main!(benches);

0 commit comments

Comments
 (0)