rustyphoenixmicrobenchmark 1.3.0

Toolset for function micro-benchmarking. Rust equivalent of C++ [PhoenixMicroBenchmark](https://gitlab.in2p3.fr/CTA-LAPP/PHOENIX_LIBS2/test-benchmark/PhoenixMicroBenchmark)
Documentation
/***************************************
	Auteur : Pierre Aubert
	Mail : pierre.aubert@lapp.in2p3.fr
	Licence : CeCILL-C
****************************************/

use std::path::Path;
use std::io::prelude::Write;
use std::io::BufWriter;

use crate::pellapsedtime::PEllapsedTime;

///Timer of a function to make nice benchmark automatically
pub struct PFunctionTimer{
	///Name of the function which is evaluated
	p_function_name: String,
	///Number of measure for each performance point
	p_nb_measure: usize,
	///Number of function calls per measure for each performance point
	p_nb_call_per_measure: usize,
	///Vector of all number of elements to measure for all performance tests
	p_vec_nb_element: Vec<usize>,
	///Vector of all measured performances
	p_vec_time: Vec<PEllapsedTime>,
}

impl PFunctionTimer{
	///Constructor of the PFunctionTimer
	/// # Parameters
	/// - `nb_measure` : number of measure for each performance point
	/// - `nb_call_per_measure` : number of function calls per measure for each performance point
	/// - `vec_nb_element` : vector of all number of element to measure function performance
	pub fn new(nb_measure: usize, nb_call_per_measure: usize, vec_nb_element: &Vec<usize>) -> Self{
		PFunctionTimer{
			p_function_name: String::from(""),
			p_nb_measure: nb_measure,
			p_nb_call_per_measure: nb_call_per_measure,
			p_vec_nb_element: vec_nb_element.clone(),
			p_vec_time: vec![]
		}
	}
	///Set the name of the function to be evaluated
	/// # Parameters
	/// - `function_name` : name of the function to be evaluated
	pub fn set_function_name(&mut self, function_name: &String){
		self.p_function_name = function_name.to_owned();
	}
	///Add a time performance of the function
	/// # Parameters
	/// - `time_perf` : time performance of the function
	pub fn add_time_perf(&mut self, time_perf: &PEllapsedTime){
		self.p_vec_time.push(time_perf.clone());
	}
	///Get the number of measure per function perf evaluation
	/// # Returns
	/// Number of measure per function perf evaluation
	pub fn get_nb_measure(&self) -> usize{
		self.p_nb_measure
	}
	///Get the evaluated function name
	/// # Returns
	/// Evaluated function name
	pub fn get_function_name(&self) -> &String{
		&self.p_function_name
	}
	///Get the number of function calls per measure per function perf evaluation
	/// # Returns
	/// Number of function calls measure per function perf evaluation
	pub fn get_nb_call_per_measure(&self) -> usize{
		self.p_nb_call_per_measure
	}
	///Get the vector of the number of element to be treated per measure
	/// # Returns
	/// Vector of the number of element to be treated per measure
	pub fn get_vec_nb_element(&self) -> &Vec<usize> {
		&self.p_vec_nb_element
	}
	///Do the full performance evaluation on all vector size
	/// # Parameters
	/// - `call_timer_eval_function` : function to call which evaluates performance for a given number of elements
	pub fn eval_perf(&mut self, call_timer_eval_function: &dyn Fn(&mut Self, usize)){
		for nb_element in self.p_vec_nb_element.clone().iter(){
			call_timer_eval_function(self, nb_element.clone());
		}
	}
	///Save performances in a gnuplot compatible file
	/// # Parameters
	/// - `filename` : text filename for gnuplot compatibility
	/// # Returns
	/// I/O result
	pub fn save_gnuplot(&self, filename: &Path) -> std::io::Result<()> {
		let mut file = BufWriter::new(std::fs::File::create(filename).unwrap());
		file.write(&format!("# PFunctionTimer : save performance of function '{}'\n", self.p_function_name).into_bytes())?;
		file.write(&format!("# nb_element\tmin_time[ns]\tmedian_time[ns]\tmax_time[ns]\n").into_bytes())?;
		for perf in self.p_vec_time.iter() {
			file.write(&format!("{:12}\t{:12}\t{:15}\t{:12}\n", perf.get_nb_element(),
				perf.get_min_ellapsed_time_ns(),
				perf.get_median_ellapsed_time_ns(),
				perf.get_max_ellapsed_time_ns()
			).into_bytes())?;
		}
		Ok(())
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use std::fs;
	use std::path::PathBuf;
	
	use crate::phoenix_function_timer;
	
	///A Hadamard product to test the micro_benchmark function
	/// # Parameters
	/// - `vec_res` : output vector of result
	/// - `vec_x` : vector of values
	/// - `vec_y` : vector of values
	fn hadamard_product(vec_res: &mut Vec<f32>, vec_x: &Vec<f32>, vec_y: &Vec<f32>){
		*vec_res = vec_x.into_iter().zip(vec_y)
			.map(|(x, y)| x * y)
			.collect::<Vec<_>>();
	}
	
	///Function which will time our hadamard_product
	/// # Parameters
	/// - `function_timer` : function timer to be used
	/// - `nb_element` : number of element of the tables
	fn timer_hadamard_product(function_timer: &mut PFunctionTimer, nb_element: usize){
		let vec_x: Vec<f32> = vec![1.0; nb_element];
		let vec_y: Vec<f32> = vec![2.0; nb_element];
		let mut vec_res: Vec<f32> = vec![0.0; nb_element];
		
		phoenix_function_timer!(function_timer, nb_element, hadamard_product(&mut vec_res, &vec_x, &vec_y));
	}
	
	///Test the PFunctionTimer
	#[test]
	fn test_pfunction_timer(){
		let mut function_timer: PFunctionTimer = PFunctionTimer::new(100, 10, &vec![1000, 1500, 2000, 2500, 3000, 4000, 5000]);
		function_timer.eval_perf(&timer_hadamard_product);
		fs::create_dir_all("generated").unwrap();
		function_timer.save_gnuplot(&PathBuf::from("generated/hadamard_product_perf.txt")).unwrap();
		assert_eq!(function_timer.get_function_name().to_owned(), String::from("hadamard_product"));
	}
}