fn format()

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(())
    }