use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;
use super::Rustics;
use super::Histogram;
use super::TimerBox;
use super::PrintOption;
use super::timer;
use super::hier_box;
use super::hier_item;
use super::running_time::RunningTime;
use super::time_window::TimeWindow;
use crate::running_integer::IntegerExporter;
use crate::Hier;
use crate::HierDescriptor;
use crate::HierConfig;
use crate::HierGenerator;
use crate::HierMember;
use crate::HierExporter;
use crate::ExporterRc;
use crate::MemberRc;
impl HierMember for RunningTime {
fn to_rustics(&self) -> &dyn Rustics {
self
}
fn to_rustics_mut(&mut self) -> &mut dyn Rustics {
self
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn to_histogram(&self) -> &dyn Histogram {
self
}
}
#[derive(Clone)]
pub struct TimeHier {
timer: TimerBox,
hz: u128,
}
pub struct TimeHierConfig {
pub name: String,
pub descriptor: HierDescriptor,
pub window_size: Option<usize>,
pub timer: TimerBox,
pub print_opts: PrintOption,
}
impl TimeHier {
pub fn new(timer: TimerBox) -> TimeHier {
let hz = timer!(timer).hz();
TimeHier { timer, hz }
}
pub fn new_hier(configuration: TimeHierConfig) -> Hier {
let generator = TimeHier::new(configuration.timer);
let generator = Rc::from(RefCell::new(generator));
let class = "time".to_string();
let descriptor = configuration.descriptor;
let name = configuration.name;
let print_opts = configuration.print_opts;
let window_size = configuration.window_size;
let config =
HierConfig { descriptor, generator, name, window_size, class, print_opts };
Hier::new(config)
}
}
impl HierGenerator for TimeHier {
fn make_member(&self, name: &str, print_opts: &PrintOption) -> MemberRc {
let member = RunningTime::new(name, self.timer.clone(), print_opts);
hier_box!(member)
}
fn make_window(&self, name: &str, window_size: usize, print_opts: &PrintOption)
-> Box<dyn Rustics> {
let window = TimeWindow::new(name, window_size, self.timer.clone(), print_opts);
Box::new(window)
}
fn make_from_exporter(&self, name: &str, print_opts: &PrintOption, exporter: ExporterRc)
-> MemberRc {
let mut exporter_borrow = exporter .borrow_mut();
let exporter_any = exporter_borrow .as_any_mut();
let exporter_impl = exporter_any .downcast_mut::<IntegerExporter>().unwrap();
let member = exporter_impl .make_member(name, print_opts);
let timer = self.timer.clone();
let member = RunningTime::from_integer(timer, print_opts, member);
hier_box!(member)
}
fn make_exporter(&self) -> ExporterRc {
let exporter = IntegerExporter::new();
Rc::from(RefCell::new(exporter))
}
fn push(&self, exporter: &mut dyn HierExporter, member_rc: MemberRc) {
let exporter_any = exporter .as_any_mut();
let exporter_impl = exporter_any .downcast_mut::<IntegerExporter>().unwrap();
let member_borrow = hier_item!(member_rc);
let member_any = member_borrow .as_any();
let member_impl = member_any .downcast_ref::<RunningTime>().unwrap();
exporter_impl.push(member_impl.export());
}
fn hz(&self) -> u128 {
self.hz
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::sync::Mutex;
use super::*;
use crate::PrintOpts;
use crate::arc_box;
use crate::hier::HierDescriptor;
use crate::hier::HierDimension;
use crate::hier::GeneratorRc;
use crate::tests::continuing_box;
use crate::tests::continuing_timer_increment;
use crate::tests::check_printer_box;
fn level_0_period() -> usize {
8
}
fn level_0_retain() -> usize {
3 * level_0_period()
}
fn make_descriptor(auto_next: i64) -> HierDescriptor {
let levels = 4;
let level_0_period = level_0_period();
let dimension = HierDimension::new(level_0_period, level_0_retain());
let mut dimensions = Vec::<HierDimension>::with_capacity(levels);
dimensions.push(dimension);
let mut period = 4;
for _i in 1..levels {
let dimension = HierDimension::new(period, 3 * period);
dimensions.push(dimension);
period += 2;
}
HierDescriptor::new(dimensions, Some(auto_next))
}
fn make_time_hier(generator: GeneratorRc, auto_next: i64, window_size: Option<usize>)
-> Hier {
let descriptor = make_descriptor(auto_next);
let class = "time".to_string();
let name = "test hier".to_string();
let print_opts = None;
let configuration =
HierConfig { descriptor, generator, class, name, window_size, print_opts };
Hier::new(configuration)
}
fn test_new_hier_arc() {
let auto_next = 200;
let descriptor = make_descriptor(auto_next);
let name = "test hier".to_string();
let print_opts = None;
let timer = continuing_box();
let window_size = None;
let configuration = TimeHierConfig { descriptor, name, window_size, timer, print_opts };
let hier = TimeHier::new_hier(configuration);
let hier = arc_box!(hier);
let mut hier_impl = hier.lock().unwrap();
let mut events = 0;
let mut sum = 0;
for i in 0..auto_next / 2 {
hier_impl.record_event();
let expected = (i + 1) * continuing_timer_increment();
assert!(hier_impl.max_i64() == expected);
sum += expected;
hier_impl.record_time(i);
sum += i;
events += 2;
}
let mean = sum as f64 / events as f64;
assert!(hier_impl.event_count() == events);
assert!(hier_impl.mean() == mean );
assert!(hier_impl.class() == "time");
hier_impl.print();
}
fn test_simple_stat() {
let timer = continuing_box();
let generator = TimeHier::new(timer);
let member_rc = generator.make_member("test member", &None);
let member_clone = member_rc.clone();
let mut member = member_clone.borrow_mut();
let value = 42;
member.to_rustics_mut().record_time(value);
assert!(member.to_rustics().count() == 1);
assert!(member.to_rustics().mean() == value as f64);
drop(member);
let exporter_rc = generator.make_exporter();
generator.push(&mut *exporter_rc.borrow_mut(), member_rc);
let new_member_rc = generator.make_from_exporter("member export", &None, exporter_rc);
let new_member = hier_item!(new_member_rc);
assert!(new_member.to_rustics().count() == 1);
assert!(new_member.to_rustics().mean() == value as f64);
assert!(new_member.to_rustics().class() == "time");
let generator = Rc::from(RefCell::new(generator));
let mut hier = make_time_hier(generator, 4, None);
let mut events = 0;
for i in 0..100 {
hier.record_time(i + 1);
events += 1;
}
assert!(hier.event_count() == events);
hier.print();
}
fn test_window() {
let auto_next = 100;
let window_size = Some(1000);
let timer = continuing_box();
let generator = TimeHier::new(timer);
let generator = Rc::from(RefCell::new(generator));
let mut hier = make_time_hier(generator, auto_next, window_size);
let period = level_0_period();
let window_size = window_size.unwrap() as i64;
let mut events = 0 as i64;
assert!( hier.int_extremes ());
assert!(!hier.float_extremes());
for i in 0..window_size {
let sample = i + 1;
hier.record_time(sample);
events += 1;
assert!(hier.count() == events as u64);
assert!(hier.max_i64() == sample );
let level_0_pushes = (events + auto_next - 1) / auto_next;
let level_0_all = std::cmp::min(level_0_pushes, level_0_retain() as i64);
let level_0_live = std::cmp::min(level_0_pushes, level_0_period() as i64);
assert!(hier.all_len (0) == level_0_all as usize);
assert!(hier.live_len(0) == level_0_live as usize);
if hier.all_len(0) > period {
assert!(hier.all_len(1) > 0);
}
assert!(hier.count() == events as u64);
}
let sum = (window_size * (window_size + 1)) / 2;
let sum = sum as f64;
let count = events as f64;
let mean = sum / count;
assert!(hier.count() == events as u64);
assert!(hier.mean() == mean );
assert!(hier.event_count() == events );
hier.record_time(window_size + 1);
assert!(hier.count() == window_size as u64);
let timer = continuing_box();
let generator = TimeHier::new(timer);
let generator = Rc::from(RefCell::new(generator));
let mut hier = make_time_hier(generator, auto_next, Some(window_size as usize));
let timer_increment = continuing_timer_increment();
let mut timer_interval = timer_increment;
let mut total_time = 0;
let mut events = 0;
{
let timer = continuing_box();
let generator = TimeHier::new(timer);
let timer = continuing_box();
let timer = timer!(timer);
assert!(generator.hz() == timer.hz());
}
for i in 1..=window_size {
hier.record_event();
assert!(hier.max_i64() == timer_interval );
assert!(hier.min_i64() == timer_increment);
assert!(hier.count() == i as u64 );
total_time += timer_interval;
timer_interval += timer_increment;
events += 1;
}
let mean = total_time as f64 / events as f64;
assert!(hier.mean() == mean );
assert!(hier.class() == "time");
{
let histogram = hier.to_log_histogram().unwrap();
let histogram = histogram.borrow();
let mut sum = 0;
for sample in histogram.positive.iter() {
sum += *sample;
}
assert!(sum == events);
}
hier.clear_all();
assert!(hier.mean() == 0.0);
hier.record_interval(&mut continuing_box());
events = 1;
assert!(hier.mean() == continuing_timer_increment() as f64);
assert!(hier.count() == events);
}
fn test_print_output() {
let expected =
[
"Test Statistics",
" Count 1,000 ",
" Minimum 1.000 microsecond",
" Maximum 1.000 millisecond",
" Log Mode 20 ",
" Mode Value 786.432 microseconds",
" Mean 500.500 microseconds",
" Std Dev 288.819 microseconds",
" Variance +8.34166 e+10 ",
" Skewness -4.16336 e-11 ",
" Kurtosis -1.19999 e+0 ",
" Log Histogram",
" -----------------------",
" 0: 0 0 0 0",
" 4: 0 0 0 0",
" 8: 0 0 1 1",
" 12: 2 4 8 16",
" 16: 33 66 131 262",
" 20: 476 0 0 0",
""
];
let timer = continuing_box();
let printer = Some(check_printer_box(&expected, true, false));
let title = None;
let units = None;
let histo_opts = None;
let print_opts = Some(PrintOpts { printer, title, units, histo_opts });
let name = "Test Statistics";
let mut stats = RunningTime::new(&name, timer, &print_opts);
let samples = 1000;
for _i in 1..=samples {
stats.record_event();
}
stats.print();
}
#[test]
fn run_tests() {
test_simple_stat ();
test_new_hier_arc();
test_window ();
test_print_output();
}
}