in resctl-bench/src/bench/iocost_qos.rs [637:880]
fn format<'a>(
&self,
out: &mut Box<dyn Write + 'a>,
data: &JobData,
opts: &FormatOpts,
props: &JobProps,
) -> Result<()> {
let mut sub_full = false;
for (k, v) in props[0].iter() {
match k.as_ref() {
"sub-full" => sub_full = v.len() == 0 || v.parse::<bool>()?,
k => bail!("unknown format parameter {:?}", k),
}
}
let rec: IoCostQoSRecord = data.parse_record()?;
let res: IoCostQoSResult = data.parse_result()?;
assert!(rec.runs.len() == res.runs.len());
if rec.runs.len() == 0
|| rec.runs[0].is_none()
|| rec.runs[0].as_ref().unwrap().qos.is_some()
{
error!("iocost-qos: Failed to format due to missing baseline");
return Ok(());
}
let base_stor_rec = &rec.runs[0].as_ref().unwrap().stor;
let base_stor_res = &res.runs[0].as_ref().unwrap().stor;
self.stor_job
.format_header(out, base_stor_rec, base_stor_res, false);
if opts.full {
for (i, (recr, resr)) in rec.runs.iter().zip(res.runs.iter()).enumerate() {
if recr.is_none() {
continue;
}
let (recr, resr) = (recr.as_ref().unwrap(), resr.as_ref().unwrap());
let qos_cfg = IoCostQoSCfg::new(&rec.base_qos, &recr.ovr);
writeln!(
out,
"\n\n{}\nQoS: {}\n",
&double_underline(&format!("RUN {:02}", i)),
qos_cfg.format()
)
.unwrap();
writeln!(out, "{}", underline(&format!("RUN {:02} - Storage", i))).unwrap();
self.stor_job.format_result(
out,
&recr.stor,
&resr.stor,
false,
&FormatOpts {
full: sub_full,
..*opts
},
);
let mut pjob = self.prot_job.clone();
Self::set_prot_size_range(&mut pjob, &recr.stor, &resr.stor);
pjob.format_result(
out,
&recr.prot,
&resr.prot,
&FormatOpts {
full: sub_full,
..*opts
},
&format!("RUN {:02} - Protection ", i),
);
writeln!(out, "\n{}", underline(&format!("RUN {:02} - Result", i))).unwrap();
StudyIoLatPcts::format_rw(out, &resr.iolat, opts, None);
if recr.qos.is_some() {
let mut cnt = 0;
write!(out, "\nvrate:").unwrap();
for pct in Self::VRATE_PCTS {
write!(
out,
" p{}={:.2}",
pct,
resr.vrate.get(&pct.to_string()).unwrap()
)
.unwrap();
cnt += 1;
if cnt % 7 == 0 {
write!(out, "\n ").unwrap();
}
}
writeln!(out, "\n").unwrap();
writeln!(
out,
"QoS result: MOF={:.3}@{}({:.3}x) vrate={:.2}:{:.2} missing={}%",
resr.stor.mem_offload_factor,
recr.stor.mem.profile,
resr.stor.mem_offload_factor / base_stor_res.mem_offload_factor,
resr.vrate["mean"],
resr.vrate["stdev"],
format_pct(Studies::reports_missing(resr.nr_reports)),
)
.unwrap();
if let Some(amof) = resr.adjusted_mem_offload_factor {
let tune_res = match &resr.prot.scenarios[0] {
protection::ScenarioResult::MemHogTune(tune_res) => tune_res,
_ => bail!("Unknown protection result: {:?}", resr.prot.scenarios[0]),
};
let hog = tune_res.final_run.as_ref().unwrap();
writeln!(
out,
" aMOF={:.3}@{}({:.3}x) isol-{}={}% lat_imp={}%:{} work_csv={}%",
amof,
recr.stor.mem.profile,
amof / base_stor_res.mem_offload_factor,
&self.isol_pct,
format_pct(hog.isol[&self.isol_pct]),
format_pct(hog.lat_imp["mean"]),
format_pct(hog.lat_imp["stdev"]),
format_pct(hog.work_csv),
)
.unwrap();
} else {
writeln!(
out,
" aMOF=FAIL isol=FAIL lat_imp=FAIL work_csv=FAIL"
)
.unwrap();
}
}
}
writeln!(out, "\n\n{}", double_underline("Summary")).unwrap();
} else {
writeln!(out, "").unwrap();
}
for (i, ovr) in self.runs.iter().enumerate() {
let qos_cfg = IoCostQoSCfg::new(&rec.base_qos, ovr);
write!(out, "[{:02}] QoS: {}", i, qos_cfg.format()).unwrap();
if ovr.off {
writeln!(out, " mem_profile={}", rec.mem_profile).unwrap();
} else {
writeln!(out, "").unwrap();
}
}
writeln!(out, "").unwrap();
writeln!(
out,
" MOF aMOF isol-{}% lat-imp% work-csv% missing%",
&self.isol_pct
)
.unwrap();
for (i, resr) in res.runs.iter().enumerate() {
match resr {
Some(resr) => {
write!(out, "[{:02}] {:>7.3} ", i, resr.stor.mem_offload_factor).unwrap();
if resr.adjusted_mem_offload_factor.is_some() {
let hog = resr.prot.scenarios[0]
.as_mem_hog_tune()
.unwrap()
.final_run
.as_ref()
.unwrap();
writeln!(
out,
"{:>7.3} {:>5.1} {:>6.1}:{:>6.1} {:>5.1} {:>5.1}",
resr.adjusted_mem_offload_factor.unwrap(),
hog.isol[&self.isol_pct] * 100.0,
hog.lat_imp["mean"] * TO_PCT,
hog.lat_imp["stdev"] * TO_PCT,
hog.work_csv * TO_PCT,
Studies::reports_missing(resr.nr_reports) * TO_PCT,
)
.unwrap();
} else {
writeln!(
out,
"{:>7} {:>5} {:>6}:{:>6} {:>5} {:>5.1}",
"FAIL",
"-",
"-",
"-",
"-",
Studies::reports_missing(resr.nr_reports) * TO_PCT,
)
.unwrap()
}
}
None => writeln!(out, "[{:02}] SKIP", i).unwrap(),
}
}
let mut format_iolat = |rw, title| {
writeln!(out, "").unwrap();
writeln!(
out,
"{:17} p50 p90 p99 max",
title
)
.unwrap();
for (i, resr) in res.runs.iter().enumerate() {
match resr {
Some(resr) => {
let iolat: &BTreeMap<String, BTreeMap<String, f64>> = &resr.iolat[rw];
writeln!(
out,
"[{:02}] {:>5}:{:>5}/{:>5} {:>5}:{:>5}/{:>5} \
{:>5}:{:>5}/{:>5} {:>5}:{:>5}/{:>5}",
i,
format_duration(iolat["50"]["mean"]),
format_duration(iolat["50"]["stdev"]),
format_duration(iolat["50"]["100"]),
format_duration(iolat["90"]["mean"]),
format_duration(iolat["90"]["stdev"]),
format_duration(iolat["90"]["100"]),
format_duration(iolat["99"]["mean"]),
format_duration(iolat["99"]["stdev"]),
format_duration(iolat["99"]["100"]),
format_duration(iolat["100"]["mean"]),
format_duration(iolat["100"]["stdev"]),
format_duration(iolat["100"]["100"])
)
.unwrap();
}
None => writeln!(out, "[{:02}] Skipped", i).unwrap(),
}
}
};
format_iolat(READ, "RLAT");
format_iolat(WRITE, "WLAT");
Ok(())
}