#![cfg(feature = "bench-zero-alloc")]
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::time::Duration;
use flowscope::Timestamp;
use netring::anomaly::Severity;
use netring::anomaly::shipped_sinks::StdoutSink;
use netring::anomaly::sink::AnomalySink;
use netring::correlate::TimeBucketedCounter;
use netring::ctx::{CounterRegistry, Ctx, FlowStateRegistry, StateMap};
use netring::monitor::{Dispatcher, HandlerRegistry};
use netring::protocol::builtin::Tcp;
use netring::protocol::event_typed::FlowStarted;
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;
#[derive(Default)]
struct Counters {
flows: u64,
}
fn build_dispatcher() -> Dispatcher {
let mut reg = HandlerRegistry::default();
reg.register::<FlowStarted<Tcp>, _, _>(|_evt: &FlowStarted<Tcp>, ctx: &mut Ctx<'_>| {
ctx.state_mut::<Counters>().flows += 1;
Ok(())
});
reg.register::<FlowStarted<Tcp>, _, _>(|_evt: &FlowStarted<Tcp>, ctx: &mut Ctx<'_>| {
let now = ctx.ts;
ctx.counter_mut::<u32>().bump(1u32, now);
Ok(())
});
reg.register::<FlowStarted<Tcp>, _, _>(|_evt: &FlowStarted<Tcp>, ctx: &mut Ctx<'_>| {
let now = ctx.ts;
ctx.sink_mut()
.write("FlowStartedTcp", Severity::Info, now, None, &[], &[]);
Ok(())
});
reg.into_dispatcher()
.expect("dispatcher build under MAX_EVENT_TYPES")
}
fn dummy_event() -> FlowStarted<Tcp> {
let key = flowscope::extract::FiveTupleKey {
proto: flowscope::L4Proto::Tcp,
a: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 12345),
b: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 80),
};
FlowStarted::<Tcp>::new_for_bench(key, Some(flowscope::L4Proto::Tcp), Timestamp::new(0, 0))
}
fn drive(
dispatcher: &mut Dispatcher,
state: &mut StateMap,
sink: &mut dyn AnomalySink,
counters: &mut CounterRegistry,
flow_states: &mut FlowStateRegistry,
evt: &FlowStarted<Tcp>,
) {
let mut ctx = Ctx::new_for_bench(Timestamp::new(0, 0), state, sink, counters, flow_states);
dispatcher
.dispatch::<FlowStarted<Tcp>>(evt, &mut ctx)
.expect("dispatch");
}
fn main() {
let _profiler = dhat::Profiler::builder().testing().build();
let mut dispatcher = build_dispatcher();
let mut state = StateMap::default();
let mut sink = StdoutSink::with_capacity(4096);
let mut counters = CounterRegistry::default();
counters.register::<u32>(TimeBucketedCounter::<u32>::new_unbounded(
Duration::from_secs(60),
Duration::from_secs(1),
));
let mut flow_states = FlowStateRegistry::default();
let evt = dummy_event();
for _ in 0..10_000 {
drive(
&mut dispatcher,
&mut state,
&mut sink,
&mut counters,
&mut flow_states,
&evt,
);
}
let before = dhat::HeapStats::get();
for _ in 0..100_000 {
drive(
&mut dispatcher,
&mut state,
&mut sink,
&mut counters,
&mut flow_states,
&evt,
);
}
let after = dhat::HeapStats::get();
let delta_bytes = after.curr_bytes as i64 - before.curr_bytes as i64;
let delta_blocks = after.curr_blocks as i64 - before.curr_blocks as i64;
eprintln!("100k synthetic dispatches: Δ {delta_bytes} bytes, Δ {delta_blocks} blocks");
assert!(
delta_bytes < 512,
"allocation regression: Δ {delta_bytes} bytes (limit 512). See dhat-heap.json."
);
assert!(
delta_blocks < 100,
"block regression: Δ {delta_blocks} blocks (limit 100). See dhat-heap.json."
);
}