1 use crate::error::Result; 2 use crate::measurement::ValueFormatter; 3 use crate::report::{BenchmarkId, MeasurementData, Report, ReportContext}; 4 use crate::Throughput; 5 use csv::Writer; 6 use std::io::Write; 7 use std::path::Path; 8 9 #[derive(Serialize)] 10 struct CsvRow<'a> { 11 group: &'a str, 12 function: Option<&'a str>, 13 value: Option<&'a str>, 14 throughput_num: Option<&'a str>, 15 throughput_type: Option<&'a str>, 16 sample_measured_value: f64, 17 unit: &'static str, 18 iteration_count: u64, 19 } 20 21 struct CsvReportWriter<W: Write> { 22 writer: Writer<W>, 23 } 24 impl<W: Write> CsvReportWriter<W> { write_data( &mut self, id: &BenchmarkId, data: &MeasurementData<'_>, formatter: &dyn ValueFormatter, ) -> Result<()>25 fn write_data( 26 &mut self, 27 id: &BenchmarkId, 28 data: &MeasurementData<'_>, 29 formatter: &dyn ValueFormatter, 30 ) -> Result<()> { 31 let mut data_scaled: Vec<f64> = data.sample_times().as_ref().into(); 32 let unit = formatter.scale_for_machines(&mut data_scaled); 33 let group = id.group_id.as_str(); 34 let function = id.function_id.as_deref(); 35 let value = id.value_str.as_deref(); 36 let (throughput_num, throughput_type) = match id.throughput { 37 Some(Throughput::Bytes(bytes)) => (Some(format!("{}", bytes)), Some("bytes")), 38 Some(Throughput::Elements(elems)) => (Some(format!("{}", elems)), Some("elements")), 39 None => (None, None), 40 }; 41 let throughput_num = throughput_num.as_deref(); 42 43 for (count, measured_value) in data.iter_counts().iter().zip(data_scaled.into_iter()) { 44 let row = CsvRow { 45 group, 46 function, 47 value, 48 throughput_num, 49 throughput_type, 50 sample_measured_value: measured_value, 51 unit, 52 iteration_count: (*count) as u64, 53 }; 54 self.writer.serialize(row)?; 55 } 56 Ok(()) 57 } 58 } 59 60 pub struct FileCsvReport; 61 impl FileCsvReport { write_file( &self, path: &Path, id: &BenchmarkId, measurements: &MeasurementData<'_>, formatter: &dyn ValueFormatter, ) -> Result<()>62 fn write_file( 63 &self, 64 path: &Path, 65 id: &BenchmarkId, 66 measurements: &MeasurementData<'_>, 67 formatter: &dyn ValueFormatter, 68 ) -> Result<()> { 69 let writer = Writer::from_path(path)?; 70 let mut writer = CsvReportWriter { writer }; 71 writer.write_data(id, measurements, formatter)?; 72 Ok(()) 73 } 74 } 75 76 impl Report for FileCsvReport { measurement_complete( &self, id: &BenchmarkId, context: &ReportContext, measurements: &MeasurementData<'_>, formatter: &dyn ValueFormatter, )77 fn measurement_complete( 78 &self, 79 id: &BenchmarkId, 80 context: &ReportContext, 81 measurements: &MeasurementData<'_>, 82 formatter: &dyn ValueFormatter, 83 ) { 84 let mut path = context.output_directory.clone(); 85 path.push(id.as_directory_name()); 86 path.push("new"); 87 path.push("raw.csv"); 88 log_if_err!(self.write_file(&path, id, measurements, formatter)); 89 } 90 } 91