use std::path::Path;
use std::convert::TryInto;
use crate::{Quantifiable,Packet,Phit,Network,Topology,ConfigurationValue,Expr,Time};
use crate::config;
#[derive(Clone,Quantifiable)]
pub struct ServerStatistics
{
pub current_measurement: ServerMeasurement,
pub cycle_last_created_phit: Time,
pub cycle_last_consumed_message: Time,
pub temporal_step: Time,
pub temporal_statistics: Vec<ServerMeasurement>,
}
#[derive(Clone,Default,Quantifiable)]
pub struct ServerMeasurement
{
pub begin_cycle: Time,
pub created_phits: usize,
pub consumed_phits: usize,
pub consumed_messages: usize,
pub total_message_delay: Time,
pub missed_generations: usize,
}
impl ServerStatistics
{
pub fn new(temporal_step:Time)->ServerStatistics
{
ServerStatistics{
current_measurement: ServerMeasurement::default(),
cycle_last_created_phit: 0,
cycle_last_consumed_message: 0,
temporal_step,
temporal_statistics: vec![],
}
}
fn reset(&mut self, next_cycle: Time)
{
self.current_measurement=ServerMeasurement::default();
self.current_measurement.begin_cycle=next_cycle;
}
pub fn track_consumed_phit(&mut self, cycle:Time)
{
self.current_measurement.consumed_phits+=1;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.consumed_phits+=1;
}
}
pub fn track_consumed_message(&mut self, cycle: Time)
{
self.cycle_last_consumed_message = cycle;
self.current_measurement.consumed_messages+=1;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.consumed_messages+=1;
}
}
pub fn track_created_phit(&mut self, cycle: Time)
{
self.current_measurement.created_phits+=1;
self.cycle_last_created_phit = cycle;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.created_phits+=1;
}
}
pub fn track_message_delay(&mut self, delay:Time, cycle: Time)
{
self.current_measurement.total_message_delay+= delay;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.total_message_delay+=delay;
}
}
pub fn track_missed_generation(&mut self, cycle: Time)
{
self.current_measurement.missed_generations+=1;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.missed_generations+=1;
}
}
pub fn current_temporal_measurement(&mut self, cycle: Time) -> Option<&mut ServerMeasurement>
{
if self.temporal_step>0
{
let index : usize = (cycle / self.temporal_step).try_into().unwrap();
if self.temporal_statistics.len()<=index
{
self.temporal_statistics.resize_with(index+1,Default::default);
self.temporal_statistics[index].begin_cycle = index as Time * self.temporal_step;
}
Some(&mut self.temporal_statistics[index])
} else { None }
}
}
#[derive(Debug,Quantifiable)]
pub struct LinkStatistics
{
pub phit_arrivals: usize,
}
impl LinkStatistics
{
fn new() -> LinkStatistics
{
LinkStatistics{
phit_arrivals: 0,
}
}
fn reset(&mut self)
{
self.phit_arrivals=0;
}
}
#[derive(Debug,Default,Quantifiable)]
pub struct StatisticMeasurement
{
pub begin_cycle: Time,
pub created_phits: usize,
pub consumed_phits: usize,
pub consumed_packets: usize,
pub consumed_messages: usize,
pub total_message_delay: Time,
pub total_packet_network_delay: Time,
pub total_packet_hops: usize,
pub total_packet_per_hop_count: Vec<usize>,
pub virtual_channel_usage: Vec<usize>,
}
pub fn jain<I:Iterator<Item=f64>>(iter:I) -> f64
{
let mut n = 0;
let mut count=0.0;
let mut count2=0.0;
for x in iter
{
n+=1;
count+=x;
count2+=x*x;
}
count*count/count2/n as f64
}
#[derive(Debug,Quantifiable)]
pub struct StatisticPacketMeasurement
{
pub consumed_cycle: Time,
pub hops: usize,
pub delay: Time,
}
#[derive(Debug,Quantifiable)]
pub struct Statistics
{
pub current_measurement: StatisticMeasurement,
pub link_statistics: Vec<Vec<LinkStatistics>>,
pub temporal_step: Time,
pub temporal_statistics: Vec<StatisticMeasurement>,
pub server_percentiles: Vec<u8>,
pub packet_percentiles: Vec<u8>,
pub packet_statistics: Vec<StatisticPacketMeasurement>,
pub columns: Vec<ReportColumn>,
pub packet_defined_statistics_definitions: Vec< (Vec<Expr>,Vec<Expr>) >,
pub packet_defined_statistics_measurement: Vec< Vec< (Vec<ConfigurationValue>,Vec<f32>,usize) >>,
}
impl Statistics
{
pub fn new(statistics_temporal_step:Time, server_percentiles: Vec<u8>, packet_percentiles: Vec<u8>, statistics_packet_definitions:Vec<(Vec<Expr>,Vec<Expr>)>, topology: &dyn Topology)->Statistics
{
let packet_defined_statistics_measurement = vec![ vec![]; statistics_packet_definitions.len() ];
Statistics{
current_measurement: Default::default(),
link_statistics: (0..topology.num_routers()).map(|i| (0..topology.ports(i)).map(|_|LinkStatistics::new()).collect() ).collect(),
temporal_step: statistics_temporal_step,
temporal_statistics: vec![],
server_percentiles,
packet_percentiles,
packet_statistics: vec![],
columns: vec![
ReportColumnKind::BeginEndCycle.into(),
ReportColumnKind::InjectedLoad.into(),
ReportColumnKind::AcceptedLoad.into(),
ReportColumnKind::AveragePacketHops.into(),
ReportColumnKind::AverageLinkUtilization.into(),
ReportColumnKind::AverageMessageDelay.into(),
ReportColumnKind::ServerGenerationJainIndex.into(),
],
packet_defined_statistics_definitions:statistics_packet_definitions,
packet_defined_statistics_measurement,
}
}
pub fn print_header(&self)
{
let report:String = self.columns.iter().map(|c|c.header()).collect();
println!("{}",report);
}
pub fn print(&self, next_cycle:Time, network:&Network)
{
let report:String = self.columns.iter().map(|c|c.format(self,next_cycle,network)).collect();
println!("{}",report);
}
pub fn reset(&mut self,next_cycle:Time, network:&mut Network)
{
self.current_measurement=Default::default();
self.current_measurement.begin_cycle=next_cycle;
for server in network.servers.iter_mut()
{
server.statistics.reset(next_cycle);
}
for router in network.routers.iter()
{
router.borrow_mut().reset_statistics(next_cycle);
}
for router_links in self.link_statistics.iter_mut()
{
for link in router_links.iter_mut()
{
link.reset();
}
}
}
pub fn track_consumed_phit(&mut self, cycle: Time)
{
self.current_measurement.consumed_phits+=1;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.consumed_phits+=1;
}
}
pub fn track_consumed_packet(&mut self, cycle: Time, packet:&Packet)
{
self.current_measurement.consumed_packets+=1;
let network_delay = cycle-*packet.cycle_into_network.borrow();
self.current_measurement.total_packet_network_delay += network_delay;
let hops=packet.routing_info.borrow().hops;
self.current_measurement.total_packet_hops+=hops;
if self.current_measurement.total_packet_per_hop_count.len() <= hops
{
self.current_measurement.total_packet_per_hop_count.resize( hops+1, 0 );
}
self.current_measurement.total_packet_per_hop_count[hops]+=1;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.consumed_packets+=1;
m.total_packet_network_delay+=network_delay;
m.total_packet_hops+=hops;
}
if !self.packet_percentiles.is_empty()
{
self.packet_statistics.push(StatisticPacketMeasurement{consumed_cycle:cycle,hops,delay:network_delay});
}
if !self.packet_defined_statistics_definitions.is_empty()
{
let be = packet.extra.borrow();
let extra = be.as_ref().unwrap();
let link_classes = extra.link_classes.iter().map(|x|ConfigurationValue::Number(*x as f64)).collect();
let switches = extra.id_switches.iter().map(|x|ConfigurationValue::Number(*x as f64)).collect();
let entry_virtual_channels = extra.entry_virtual_channels.iter().map(|x|match x{
Some(v) => ConfigurationValue::Number(*v as f64),
None => ConfigurationValue::None,
}).collect();
let cycle_per_hop = extra.cycle_per_hop.iter().map(|x|ConfigurationValue::Number(*x as f64)).collect();
let context_content = vec![
(String::from("hops"), ConfigurationValue::Number(hops as f64)),
(String::from("delay"), ConfigurationValue::Number(network_delay as f64)),
(String::from("cycle_into_network"), ConfigurationValue::Number(*packet.cycle_into_network.borrow() as f64)),
(String::from("size"), ConfigurationValue::Number(packet.size as f64)),
(String::from("link_classes"), ConfigurationValue::Array(link_classes)),
(String::from("switches"), ConfigurationValue::Array(switches)),
(String::from("entry_virtual_channels"), ConfigurationValue::Array(entry_virtual_channels)),
(String::from("cycle_per_hop"), ConfigurationValue::Array(cycle_per_hop)),
];
let context = ConfigurationValue::Object( String::from("packet"), context_content );
let path = Path::new(".");
for (index,definition) in self.packet_defined_statistics_definitions.iter().enumerate()
{
let key : Vec<ConfigurationValue> = definition.0.iter().map(|key_expr|config::evaluate( key_expr, &context, path).unwrap_or_else(|error|panic!("error building user defined statistics: {}",error))).collect();
let value : Vec<f32> = definition.1.iter().map(|key_expr|
match config::evaluate( key_expr, &context, path).unwrap_or_else(|error|panic!("error building user defined statistics: {}",error)){
ConfigurationValue::Number(x) => x as f32,
_ => 0f32,
}).collect();
let measurement = self.packet_defined_statistics_measurement[index].iter_mut().find(|m|m.0==key);
match measurement
{
Some(m) =>
{
for (iv,v) in m.1.iter_mut().enumerate()
{
*v += value[iv];
}
m.2+=1;
}
None => {
self.packet_defined_statistics_measurement[index].push( (key,value,1) )
},
};
}
}
}
pub fn track_consumed_message(&mut self, cycle: Time)
{
self.current_measurement.consumed_messages+=1;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.consumed_messages+=1;
}
}
pub fn track_created_phit(&mut self, cycle: Time)
{
self.current_measurement.created_phits+=1;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.created_phits+=1;
}
}
pub fn track_message_delay(&mut self, delay:Time, cycle: Time)
{
self.current_measurement.total_message_delay+= delay;
if let Some(m) = self.current_temporal_measurement(cycle)
{
m.total_message_delay+=delay;
}
}
pub fn track_phit_hop(&mut self, phit:&Phit, cycle: Time)
{
let vc:usize = phit.virtual_channel.borrow().unwrap();
if self.current_measurement.virtual_channel_usage.len() <= vc
{
self.current_measurement.virtual_channel_usage.resize(vc+1, 0);
}
self.current_measurement.virtual_channel_usage[vc]+=1;
if let Some(m) = self.current_temporal_measurement(cycle)
{
if m.virtual_channel_usage.len() <= vc
{
m.virtual_channel_usage.resize(vc+1, 0);
}
m.virtual_channel_usage[vc]+=1;
}
}
pub fn current_temporal_measurement(&mut self, cycle: Time) -> Option<&mut StatisticMeasurement>
{
if self.temporal_step>0
{
let index : usize = (cycle / self.temporal_step).try_into().unwrap();
if self.temporal_statistics.len()<=index
{
self.temporal_statistics.resize_with(index+1,Default::default);
self.temporal_statistics[index].begin_cycle = index as Time * self.temporal_step;
}
Some(&mut self.temporal_statistics[index])
} else { None }
}
}
#[derive(Debug,Quantifiable)]
#[allow(dead_code)]
enum ReportColumnKind
{
BeginEndCycle,
InjectedLoad,
AcceptedLoad,
ServerGenerationJainIndex,
ServerConsumptionJainIndex,
AverageMessageDelay,
AveragePacketNetworkDelay,
AveragePacketHops,
AverageLinkUtilization,
MaximumLinkUtilization,
}
impl ReportColumnKind
{
fn name(&self) -> &str
{
match self
{
ReportColumnKind::BeginEndCycle => "cycle_begin-cycle_end",
ReportColumnKind::InjectedLoad => "injected_load",
ReportColumnKind::AcceptedLoad => "accepted_load",
ReportColumnKind::ServerGenerationJainIndex => "server_generation_jain_index",
ReportColumnKind::ServerConsumptionJainIndex => "server_consumption_jain_index",
ReportColumnKind::AverageMessageDelay => "average_message_delay",
ReportColumnKind::AveragePacketNetworkDelay => "average_packet_network_delay",
ReportColumnKind::AveragePacketHops => "average_packet_hops",
ReportColumnKind::AverageLinkUtilization => "average_link_utilization",
ReportColumnKind::MaximumLinkUtilization => "maximum_link_utilization",
}
}
}
#[derive(Debug,Quantifiable)]
pub struct ReportColumn
{
kind: ReportColumnKind,
width: usize,
}
impl ReportColumn
{
fn header(&self) -> String
{
let base = self.kind.name();
format!("{name:width$}",name=base,width=self.width)
}
fn format(&self, statistics: &Statistics, next_cycle: Time, network:&Network) -> String
{
let cycles=next_cycle-statistics.current_measurement.begin_cycle+1;
let value = match self.kind
{
ReportColumnKind::BeginEndCycle => format!("{:>11}-{}",statistics.current_measurement.begin_cycle,next_cycle-1),
ReportColumnKind::InjectedLoad => format!{"{}",statistics.current_measurement.created_phits as f32/cycles as f32/network.servers.len() as f32},
ReportColumnKind::AcceptedLoad => format!{"{}",statistics.current_measurement.consumed_phits as f32/cycles as f32/network.servers.len() as f32},
ReportColumnKind::ServerGenerationJainIndex => format!{"{}",network.jain_server_created_phits()},
ReportColumnKind::ServerConsumptionJainIndex => format!{"{}",network.jain_server_consumed_phits()},
ReportColumnKind::AverageMessageDelay => format!("{}",statistics.current_measurement.total_message_delay as f64/statistics.current_measurement.consumed_messages as f64),
ReportColumnKind::AveragePacketNetworkDelay => format!("{}",statistics.current_measurement.total_packet_network_delay as f64/statistics.current_measurement.consumed_packets as f64),
ReportColumnKind::AveragePacketHops => format!("{}",statistics.current_measurement.total_packet_hops as f64 / statistics.current_measurement.consumed_packets as f64),
ReportColumnKind::AverageLinkUtilization =>
{
let total_arrivals:usize = (0..network.topology.num_routers()).map(|i|(0..network.topology.degree(i)).map(|j|statistics.link_statistics[i][j].phit_arrivals).sum::<usize>()).sum();
let total_links: usize = (0..network.topology.num_routers()).map(|i|network.topology.degree(i)).sum();
format!("{}",total_arrivals as f64 / cycles as f64 / total_links as f64)
},
ReportColumnKind::MaximumLinkUtilization =>
{
let maximum_arrivals:usize = statistics.link_statistics.iter().map(|rls|rls.iter().map(|ls|ls.phit_arrivals).max().unwrap()).max().unwrap();
format!("{}",maximum_arrivals as f64 / cycles as f64)
},
};
format!("{value:width$}",value=value,width=self.width)
}
}
impl From<ReportColumnKind> for ReportColumn
{
fn from(kind:ReportColumnKind) -> ReportColumn
{
let width = 1+kind.name().len();
ReportColumn{
kind,
width,
}
}
}