This PR adds support for the new AEAD cryptosystems advertised by Discord, AES256-GCM and XChaCha20Poly1305. These schemes will shortly become mandatory, and provider stronger integrity/authentication guarantees over the cleartext portions of any voice packet by correctly specifying additional authenticated data. To provide smooth switchover, we've added basic negotiation over the `CryptoMode`. This ensures that any clients who are manually specifying one of the legacy modes will automatically migrate to `Aes256Gcm` when Discord cease to advertise their original preference. Closes #246. --------- Co-authored-by: Kyle Simpson <kyleandrew.simpson@gmail.com>
341 lines
10 KiB
Rust
341 lines
10 KiB
Rust
use std::error::Error;
|
|
|
|
use criterion::{
|
|
black_box,
|
|
criterion_group,
|
|
criterion_main,
|
|
BatchSize,
|
|
Bencher,
|
|
BenchmarkId,
|
|
Criterion,
|
|
};
|
|
use flume::{Receiver, Sender, TryRecvError};
|
|
use songbird::{
|
|
constants::*,
|
|
driver::{
|
|
bench_internals::{
|
|
self,
|
|
mixer::{state::InputState, Mixer},
|
|
scheduler::*,
|
|
task_message::*,
|
|
CryptoState,
|
|
},
|
|
Bitrate,
|
|
DummyMixer,
|
|
Listeners,
|
|
MockScheduler,
|
|
},
|
|
input::{cached::Compressed, codecs::*, Input, RawAdapter},
|
|
tracks,
|
|
Config,
|
|
};
|
|
use std::{io::Cursor, net::UdpSocket, sync::Arc};
|
|
use tokio::runtime::{Handle, Runtime};
|
|
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(MockScheduler::from_mixers(
|
|
None,
|
|
vec![Mixer::test_with_float(*i, rt.handle().clone(), true)],
|
|
))
|
|
},
|
|
|input| {
|
|
black_box(input.0.core.run_once());
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
},
|
|
);
|
|
group.bench_with_input(
|
|
BenchmarkId::new("Single Packet (No Soft-Clip)", track_count),
|
|
&track_count,
|
|
|b, i| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
vec![Mixer::test_with_float(*i, rt.handle().clone(), false)],
|
|
))
|
|
},
|
|
|input| {
|
|
black_box(input.0.core.run_once());
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
},
|
|
);
|
|
group.bench_with_input(
|
|
BenchmarkId::new("n=5 Packets", track_count),
|
|
&track_count,
|
|
|b, i| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
vec![Mixer::test_with_float(*i, rt.handle().clone(), true)],
|
|
))
|
|
},
|
|
|input| {
|
|
for i in 0..5 {
|
|
black_box(input.0.core.run_once());
|
|
}
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn no_passthrough_multimix(c: &mut Criterion) {
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
const N_MIXERS: usize = 16;
|
|
let mut group = c.benchmark_group(format!("Float Input (No Passthrough, {N_MIXERS} mixers)"));
|
|
|
|
for shift in 0..=2 {
|
|
let track_count = 1 << shift;
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::new("Single Packet (No Soft-Clip)", track_count),
|
|
&track_count,
|
|
|b, i| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
(0..N_MIXERS)
|
|
.map(|_| Mixer::test_with_float(*i, rt.handle().clone(), false))
|
|
.collect(),
|
|
))
|
|
},
|
|
|input| {
|
|
black_box(input.0.core.run_once());
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
},
|
|
);
|
|
group.bench_with_input(
|
|
BenchmarkId::new("n=5 Packets", track_count),
|
|
&track_count,
|
|
|b, i| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
(0..N_MIXERS)
|
|
.map(|_| Mixer::test_with_float(*i, rt.handle().clone(), false))
|
|
.collect(),
|
|
))
|
|
},
|
|
|input| {
|
|
for i in 0..5 {
|
|
black_box(input.0.core.run_once());
|
|
}
|
|
},
|
|
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(MockScheduler::from_mixers(
|
|
None,
|
|
vec![Mixer::test_with_opus(rt.handle().clone())],
|
|
))
|
|
},
|
|
|input| {
|
|
black_box(input.0.core.run_once());
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
});
|
|
group.bench_function("n=5 Packets", |b| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
vec![Mixer::test_with_opus(rt.handle().clone())],
|
|
))
|
|
},
|
|
|input| {
|
|
for i in 0..5 {
|
|
black_box(input.0.core.run_once());
|
|
}
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn passthrough_multimix(c: &mut Criterion) {
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
const N_MIXERS: usize = 16;
|
|
let mut group = c.benchmark_group(format!("Opus Input (Passthrough, {N_MIXERS} mixers)"));
|
|
|
|
for shift in 0..=2 {
|
|
let track_count = 1 << shift;
|
|
|
|
group.bench_with_input(
|
|
BenchmarkId::new("Single Packet (No Soft-Clip)", track_count),
|
|
&track_count,
|
|
|b, i| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
(0..N_MIXERS)
|
|
.map(|_| Mixer::test_with_opus(rt.handle().clone()))
|
|
.collect(),
|
|
))
|
|
},
|
|
|input| {
|
|
black_box(input.0.core.run_once());
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
},
|
|
);
|
|
group.bench_with_input(
|
|
BenchmarkId::new("n=5 Packets", track_count),
|
|
&track_count,
|
|
|b, i| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
(0..N_MIXERS)
|
|
.map(|_| Mixer::test_with_opus(rt.handle().clone()))
|
|
.collect(),
|
|
))
|
|
},
|
|
|input| {
|
|
for i in 0..5 {
|
|
black_box(input.0.core.run_once());
|
|
}
|
|
},
|
|
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(MockScheduler::from_mixers(
|
|
None,
|
|
vec![Mixer::test_with_float_drop(15, rt.handle().clone())],
|
|
))
|
|
},
|
|
|input| {
|
|
for i in 0..5 {
|
|
black_box(input.0.core.run_once());
|
|
}
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
});
|
|
}
|
|
|
|
fn task_culling(c: &mut Criterion) {
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
const N_MIXERS: usize = 8;
|
|
|
|
c.bench_function("Live Mixer Thread Culling", |b| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
(0..N_MIXERS)
|
|
.map(|_| Mixer::test_with_opus(rt.handle().clone()))
|
|
.collect(),
|
|
))
|
|
},
|
|
|input| {
|
|
black_box(input.0.core.remove_task(0));
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
});
|
|
|
|
c.bench_function("Live Mixer Thread Culling (Practical)", |b| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
(0..N_MIXERS)
|
|
.map(|_| Mixer::test_with_opus(rt.handle().clone()))
|
|
.collect(),
|
|
))
|
|
},
|
|
|input| {
|
|
black_box({
|
|
input.0.core.mark_for_cull(0);
|
|
input.0.core.mark_for_cull(1);
|
|
input.0.core.mark_for_cull(4);
|
|
input.0.core.demote_and_remove_mixers();
|
|
});
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
});
|
|
|
|
c.bench_function("Live Mixer Thread Culling (Practical, NoDel)", |b| {
|
|
b.iter_batched_ref(
|
|
|| {
|
|
black_box(MockScheduler::from_mixers(
|
|
None,
|
|
(0..N_MIXERS)
|
|
.map(|_| Mixer::test_with_opus(rt.handle().clone()))
|
|
.collect(),
|
|
))
|
|
},
|
|
|input| {
|
|
black_box(input.0.core.demote_and_remove_mixers());
|
|
},
|
|
BatchSize::SmallInput,
|
|
)
|
|
});
|
|
}
|
|
|
|
criterion_group!(individual, no_passthrough, passthrough);
|
|
criterion_group!(multimix, no_passthrough_multimix, passthrough_multimix);
|
|
criterion_group!(deletions, culling, task_culling);
|
|
criterion_main!(individual, multimix, deletions);
|