use bytes::Bytes;
use rml_rtmp::chunk_io::ChunkSerializer;
use rml_rtmp::messages::RtmpMessage;
use rml_rtmp::time::RtmpTimestamp;
use std::collections::VecDeque;
use std::hint::black_box;
use std::time::{Duration, Instant};
const CHUNK_SIZE: u32 = 4096;
fn new_serializer() -> ChunkSerializer {
let mut serializer = ChunkSerializer::new();
let _ = serializer
.set_max_chunk_size(CHUNK_SIZE, RtmpTimestamp::new(0))
.expect("chunk size is valid");
serializer
}
fn bench<F: FnMut()>(runs: usize, mut f: F) -> f64 {
let target = Duration::from_millis(60);
let warmup = Instant::now();
while warmup.elapsed() < Duration::from_millis(20) {
f();
}
let mut samples: Vec<f64> = (0..runs)
.map(|_| {
let t0 = Instant::now();
let mut n = 0u64;
loop {
f();
n += 1;
if n.is_multiple_of(16) && t0.elapsed() >= target {
break;
}
}
t0.elapsed().as_nanos() as f64 / n as f64
})
.collect();
samples.sort_by(|a, b| a.partial_cmp(b).expect("sample is finite"));
samples[samples.len() / 2]
}
fn make_payload(size: usize) -> Bytes {
let mut v = vec![0u8; size];
let mut x: u32 = 0x12345678;
for b in v.iter_mut() {
x = x.wrapping_mul(1664525).wrapping_add(1013904223);
*b = (x >> 24) as u8;
}
v[0] = 0x17;
Bytes::from(v)
}
#[test]
#[ignore]
fn bench_fanout_serialize_vs_clone() {
println!("payload_size,serialize_ns,clone_enqueue_ns,serialized_wire_len,ratio");
let sizes = [512usize, 4096, 16 * 1024, 64 * 1024, 256 * 1024];
for &size in &sizes {
let payload = make_payload(size);
let mut serializer = new_serializer();
let mut ts: u32 = 1000;
let mut wire_len = 0usize;
let serialize_ns = bench(7, || {
ts = ts.wrapping_add(33);
let msg = RtmpMessage::VideoData {
data: payload.clone(),
};
let mp = msg
.into_message_payload(RtmpTimestamp::new(ts), 1)
.expect("payload conversion");
let packet = serializer
.serialize(&mp, false, true)
.expect("media serialize");
wire_len = packet.bytes.len();
black_box(&packet);
});
let shared: Bytes = {
let mut one_shot = new_serializer();
let msg = RtmpMessage::VideoData {
data: payload.clone(),
};
let mp = msg
.into_message_payload(RtmpTimestamp::new(1000), 1)
.expect("payload conversion");
Bytes::from(
one_shot
.serialize(&mp, false, true)
.expect("media serialize")
.bytes,
)
};
let mut queue: VecDeque<(Bytes, usize, Instant)> = VecDeque::with_capacity(1 << 20);
let mut total_bytes = 0usize;
let clone_ns = bench(7, || {
let b = shared.clone();
total_bytes += b.len();
queue.push_back((b, 0, Instant::now()));
if queue.len() >= (1 << 19) {
queue.clear(); }
black_box(total_bytes);
});
println!(
"{},{:.1},{:.1},{},{:.1}x",
size,
serialize_ns,
clone_ns,
wire_len,
serialize_ns / clone_ns
);
}
}
#[test]
#[ignore]
fn bench_fanout_aggregate_model() {
println!("fanout_model,W,current_us_per_packet,shared_us_per_packet");
let payload = make_payload(16 * 1024);
for &w in &[10usize, 100, 1000] {
let mut sessions: Vec<ChunkSerializer> = (0..w).map(|_| new_serializer()).collect();
let mut ts = 1000u32;
let current_ns = bench(5, || {
ts = ts.wrapping_add(33);
for s in sessions.iter_mut() {
let msg = RtmpMessage::VideoData {
data: payload.clone(),
};
let mp = msg
.into_message_payload(RtmpTimestamp::new(ts), 1)
.expect("payload conversion");
black_box(s.serialize(&mp, false, true).expect("media serialize"));
}
});
let mut serializer = new_serializer();
let mut queues: Vec<VecDeque<(Bytes, usize, Instant)>> =
(0..w).map(|_| VecDeque::with_capacity(4096)).collect();
let shared_ns = bench(5, || {
ts = ts.wrapping_add(33);
let msg = RtmpMessage::VideoData {
data: payload.clone(),
};
let mp = msg
.into_message_payload(RtmpTimestamp::new(ts), 1)
.expect("payload conversion");
let shared = Bytes::from(
serializer
.serialize(&mp, false, true)
.expect("media serialize")
.bytes,
);
for q in queues.iter_mut() {
q.push_back((shared.clone(), 0, Instant::now()));
if q.len() >= 2048 {
q.clear();
}
}
});
println!(
"fanout,{},{:.1},{:.1}",
w,
current_ns / 1000.0,
shared_ns / 1000.0
);
}
}
#[test]
#[ignore]
fn bench_fanout_reference_costs() {
println!("reference,op,ns_per_op");
let src = make_payload(16 * 1024);
let alloc_memcpy_ns = bench(7, || {
let mut v = Vec::with_capacity(src.len());
v.extend_from_slice(&src);
black_box(&v);
});
println!("reference,alloc_plus_memcpy_16k,{:.1}", alloc_memcpy_ns);
let clone_ns = bench(7, || {
black_box(black_box(&src).clone()); });
println!("reference,bytes_clone_drop,{:.2}", clone_ns);
let now_ns = bench(7, || {
black_box(Instant::now());
});
println!("reference,instant_now,{:.2}", now_ns);
}