Files
songbird/benches/mixing-task.rs
2021-07-01 11:55:40 +01:00

240 lines
5.9 KiB
Rust

use criterion::{
black_box,
criterion_group,
criterion_main,
BatchSize,
Bencher,
BenchmarkId,
Criterion,
};
use flume::{Receiver, Sender, TryRecvError};
use songbird::{
constants::*,
driver::{
bench_internals::{mixer::Mixer, task_message::*, CryptoState},
Bitrate,
},
input::{cached::Compressed, Input},
tracks,
};
use tokio::runtime::{Handle, Runtime};
use xsalsa20poly1305::{aead::NewAead, XSalsa20Poly1305 as Cipher, KEY_SIZE};
// create a dummied task + interconnect.
// measure perf at varying numbers of sources (binary 1--64) without passthrough support.
fn dummied_mixer(
handle: Handle,
) -> (
Mixer,
(
Receiver<CoreMessage>,
Receiver<EventMessage>,
Receiver<UdpRxMessage>,
Receiver<UdpTxMessage>,
),
) {
let (mix_tx, mix_rx) = flume::unbounded();
let (core_tx, core_rx) = flume::unbounded();
let (event_tx, event_rx) = flume::unbounded();
let (udp_sender_tx, udp_sender_rx) = flume::unbounded();
let (udp_receiver_tx, udp_receiver_rx) = flume::unbounded();
let ic = Interconnect {
core: core_tx,
events: event_tx,
mixer: mix_tx,
};
let mut out = Mixer::new(mix_rx, handle, ic, Default::default());
let fake_conn = MixerConnection {
cipher: Cipher::new_from_slice(&vec![0u8; KEY_SIZE]).unwrap(),
crypto_state: CryptoState::Normal,
udp_rx: udp_receiver_tx,
udp_tx: udp_sender_tx,
};
out.conn_active = Some(fake_conn);
out.skip_sleep = true;
(out, (core_rx, event_rx, udp_receiver_rx, udp_sender_rx))
}
fn mixer_float(
num_tracks: usize,
handle: Handle,
) -> (
Mixer,
(
Receiver<CoreMessage>,
Receiver<EventMessage>,
Receiver<UdpRxMessage>,
Receiver<UdpTxMessage>,
),
) {
let mut out = dummied_mixer(handle);
let floats = utils::make_sine(10 * STEREO_FRAME_SIZE, true);
let mut tracks = vec![];
for i in 0..num_tracks {
let input = Input::float_pcm(true, floats.clone().into());
tracks.push(tracks::create_player(input).0.into());
}
out.0.tracks = tracks;
out
}
fn mixer_float_drop(
num_tracks: usize,
handle: Handle,
) -> (
Mixer,
(
Receiver<CoreMessage>,
Receiver<EventMessage>,
Receiver<UdpRxMessage>,
Receiver<UdpTxMessage>,
),
) {
let mut out = dummied_mixer(handle);
let mut tracks = vec![];
for i in 0..num_tracks {
let floats = utils::make_sine((i / 5) * STEREO_FRAME_SIZE, true);
let input = Input::float_pcm(true, floats.clone().into());
tracks.push(tracks::create_player(input).0.into());
}
out.0.tracks = tracks;
out
}
fn mixer_opus(
handle: Handle,
) -> (
Mixer,
(
Receiver<CoreMessage>,
Receiver<EventMessage>,
Receiver<UdpRxMessage>,
Receiver<UdpTxMessage>,
),
) {
// should add a single opus-based track.
// make this fully loaded to prevent any perf cost there.
let mut out = dummied_mixer(handle);
let floats = utils::make_sine(6 * STEREO_FRAME_SIZE, true);
let mut tracks = vec![];
let mut src = Compressed::new(
Input::float_pcm(true, floats.clone().into()),
Bitrate::BitsPerSecond(128_000),
)
.expect("These parameters are well-defined.");
src.raw.load_all();
tracks.push(tracks::create_player(src.into()).0.into());
out.0.tracks = tracks;
out
}
fn no_passthrough(c: &mut Criterion) {
let rt = Runtime::new().unwrap();
let mut group = c.benchmark_group("Float Input (No Passthrough)");
for shift in 0..=6 {
let track_count = 1 << shift;
group.bench_with_input(
BenchmarkId::new("Single Packet", track_count),
&track_count,
|b, i| {
b.iter_batched_ref(
|| black_box(mixer_float(*i, rt.handle().clone())),
|input| {
black_box(input.0.cycle());
},
BatchSize::SmallInput,
)
},
);
group.bench_with_input(
BenchmarkId::new("n=5 Packets", track_count),
&track_count,
|b, i| {
b.iter_batched_ref(
|| black_box(mixer_float(*i, rt.handle().clone())),
|input| {
for i in 0..5 {
black_box(input.0.cycle());
}
},
BatchSize::SmallInput,
)
},
);
}
group.finish();
}
fn passthrough(c: &mut Criterion) {
let rt = Runtime::new().unwrap();
let mut group = c.benchmark_group("Opus Input (Passthrough)");
group.bench_function("Single Packet", |b| {
b.iter_batched_ref(
|| black_box(mixer_opus(rt.handle().clone())),
|input| {
black_box(input.0.cycle());
},
BatchSize::SmallInput,
)
});
group.bench_function("n=5 Packets", |b| {
b.iter_batched_ref(
|| black_box(mixer_opus(rt.handle().clone())),
|input| {
for i in 0..5 {
black_box(input.0.cycle());
}
},
BatchSize::SmallInput,
)
});
group.finish();
}
fn culling(c: &mut Criterion) {
let rt = Runtime::new().unwrap();
c.bench_function("Worst-case Track Culling (15 tracks, 5 pkts)", |b| {
b.iter_batched_ref(
|| black_box(mixer_float_drop(15, rt.handle().clone())),
|input| {
for i in 0..5 {
black_box(input.0.cycle());
}
},
BatchSize::SmallInput,
)
});
}
criterion_group!(benches, no_passthrough, passthrough, culling);
criterion_main!(benches);