#![cfg_attr(feature = "cargo-clippy", deny(warnings))] #![allow(unknown_lints)] #![allow(many_single_char_names)] extern crate getopts; #[macro_use] extern crate log; extern crate tic; extern crate time; use getopts::Options; use log::{LogLevel, LogLevelFilter, LogMetadata, LogRecord}; use std::{env, fmt, thread}; use tic::{Clocksource, HttpReporter, Interest, Percentile, Receiver, Sample, Sender}; use tic::SECOND; #[derive(Clone, PartialEq, Eq, Hash)] pub enum Metric { Ok, } impl fmt::Display for Metric { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Metric::Ok => write!(f, "ok"), } } } struct Generator { stats: Sender, clocksource: Clocksource, } impl Generator { fn new(stats: Sender, clocksource: Clocksource) -> Generator { Generator { stats: stats, clocksource: clocksource, } } fn run(&mut self) { let mut t1 = time::precise_time_ns() + SECOND as u64; loop { let t = time::precise_time_ns(); if t > t1 { let t2 = self.clocksource.time(); trace!("sample: ref: {} tsc: {}", t, t2); self.stats.send(Sample::new(t, t2, Metric::Ok)).unwrap(); t1 += SECOND as u64; } } } } pub struct SimpleLogger; impl log::Log for SimpleLogger { fn enabled(&self, metadata: &LogMetadata) -> bool { metadata.level() <= LogLevel::Trace } fn log(&self, record: &LogRecord) { if self.enabled(record.metadata()) { println!( "{} {:<5} [{}] {}", time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).unwrap(), record.level().to_string(), "allan", record.args() ); } } } fn set_log_level(level: usize) { let log_filter; match level { 0 => { log_filter = LogLevelFilter::Info; } 1 => { log_filter = LogLevelFilter::Debug; } _ => { log_filter = LogLevelFilter::Trace; } } let _ = log::set_logger(|max_log_level| { max_log_level.set(log_filter); Box::new(SimpleLogger) }); } fn print_usage(program: &str, opts: &Options) { let brief = format!("Usage: {} [options]", program); print!("{}", opts.usage(&brief)); } pub fn opts() -> Options { let mut opts = Options::new(); opts.optopt("w", "windows", "number of integration windows", "INTEGER"); opts.optflag("h", "help", "print this help menu"); opts } fn main() { let args: Vec = env::args().collect(); let program = &args[0]; let opts = opts(); let matches = match opts.parse(&args[1..]) { Ok(m) => m, Err(f) => { error!("Failed to parse command line args: {}", f); return; } }; if matches.opt_present("help") { print_usage(program, &opts); return; } set_log_level(1); info!("tic benchmark"); let windows = matches .opt_str("windows") .unwrap_or_else(|| "300".to_owned()) .parse() .unwrap(); let duration = 1; let capacity = 128; let batch = 1; // initialize a Receiver for the benchmark let mut receiver = Receiver::configure() .windows(windows) .duration(duration) .capacity(capacity) .batch_size(batch) .service(true) .build(); let mut http = HttpReporter::new(&receiver, "localhost:42024"); thread::spawn(move || http.run()); receiver.add_interest(Interest::LatencyWaterfall( Metric::Ok, "ok_waterfall.png".to_owned(), )); receiver.add_interest(Interest::LatencyTrace( Metric::Ok, "ok_trace.txt".to_owned(), )); receiver.add_interest(Interest::Count(Metric::Ok)); receiver.add_interest(Interest::LatencyPercentile(Metric::Ok)); receiver.add_interest(Interest::AllanDeviation(Metric::Ok)); let sender = receiver.get_sender(); let clocksource = receiver.get_clocksource(); let s = sender.clone(); let c = clocksource.clone(); thread::spawn(move || { Generator::new(s, c).run(); }); let mut total = 0; let windows = windows; // we run the receiver manually so we can access the Meters for _ in 0..windows { let t0 = clocksource.time(); receiver.run_once(); let t1 = clocksource.time(); let m = receiver.clone_meters(); let mut c = 0; if let Some(t) = m.count(&Metric::Ok) { c = *t - total; total = *t; } let r = c as f64 / ((t1 - t0) as f64 / 1_000_000_000.0); info!("rate: {} samples per second", r); info!( "phase offset (ns): p50: {} p90: {} p999: {} p9999: {} max: {}", m.latency_percentile(&Metric::Ok, Percentile("p50".to_owned(), 50.0)) .unwrap_or(&0), m.latency_percentile(&Metric::Ok, Percentile("p90".to_owned(), 90.0)) .unwrap_or(&0), m.latency_percentile(&Metric::Ok, Percentile("p999".to_owned(), 99.9)) .unwrap_or(&0), m.latency_percentile(&Metric::Ok, Percentile("p9999".to_owned(), 99.99)) .unwrap_or(&0), m.latency_percentile(&Metric::Ok, Percentile("max".to_owned(), 100.0)) .unwrap_or(&0) ); for t in 1..10 { info!( "ADEV: t={}: {}", t, m.adev(Metric::Ok, t).unwrap_or(&0.0) ); } for t in 10..21 { info!("ADEV: t={}: {}", t, m.adev(Metric::Ok, t).unwrap_or(&0.0)); } for t in 3..10 { let t = t * 10; info!("ADEV: t={}: {}", t, m.adev(Metric::Ok, t).unwrap_or(&0.0)); } for t in 1..4 { let t = t * 100; info!("ADEV: t={}: {}", t, m.adev(Metric::Ok, t).unwrap_or(&0.0)); } } info!("saving files..."); receiver.save_files(); info!("saved"); }