Plot FIO Benchmark Results
Generates a 4-panel PNG chart from fio benchmark markdown result files produced by CIChurn.fio_distributed.
When to Use
- After running one or more
fio_distributed scenarios
- When results files exist in
results/ (or a custom directory)
- To compare throughput, IOPS, and latency across different client counts
Output
<results_dir>/fio_benchmark_results.png — 4-panel chart saved in the same directory as the input files
- Panel 1: Aggregate (sum) bandwidth per run, sorted by client count
- Panel 2: Aggregate IOPS per run
- Panel 3: Avg vs P99 latency per run (grouped bar)
- Panel 4: Actual bandwidth vs ideal linear scaling (saturation gap shaded)
- All panels: x-axis shows client count numbers only (e.g.
3, 4, 9, 20), labelled "FIO Client Count" — no filenames, no rotation
Procedure
Locate result files — accepts an optional directory argument (default: results/ relative to workspace root); files may be in dated subdirectories (e.g. results/apr7/); each file is a markdown document with a ## Summary Table section
Parse each file — extract the summary row using this regex pattern:
^\| (\d+)\s+\| (\d+)\s+\| (\d+)\s+\| (\S+)\s+\| (\S+)\s+\| (\S+)\s+\| (\d+)\s+\| (\d+)\s+
\| ([\d.]+ \S+/s)\s+\| ([\d.]+ \S+/s)\s+\| ([\d.]+ \S+/s)\s+\| ([\d.]+ \S+/s)\s+
\| ([\d.k]+)\s+\| ([\d.k]+)\s+\| ([\d.k]+)\s+\| ([\d.k]+)\s+\| ([\d.]+)\s+\| ([\d.]+)
Fields: clients, vols_per_client, total_vols, profile, rw, bs, numjobs, iodepth, bw_sum, bw_avg, bw_max, bw_min, iops_sum, iops_avg, iops_max, iops_min, avg_lat_ms, p99_lat_ms
Normalise bandwidth — convert all BW values to MiB/s:
GiB/s × 1024, MiB/s × 1, KiB/s ÷ 1024
Normalise IOPS — strip trailing k and multiply by 1000
Sort rows by clients ascending
Run the plot script — use [./scripts/plot_fio_results.py](./scripts/plot_fio_results.py) (already present in scripts/), passing the results directory as an argument:
uv run --with matplotlib scripts/plot_fio_results.py <results_dir>
e.g. uv run --with matplotlib scripts/plot_fio_results.py results/apr7
The script uses matplotlib.use("Agg") — no display required.
Confirm output — verify <results_dir>/fio_benchmark_results.png was saved in the same folder as the input files
Key Observations to Report
After generating the chart, summarise:
- At what client count throughput/IOPS plateaued (storage saturation point)
- Latency degradation factor from lowest to highest client count
- Whether results are from a single profile or mixed (flag mixed profiles)
Result File Format Reference
## Summary Table
| Client Nodes | Volumes/Client | Total Volumes | Profile | RW | Block Size | NumJobs | IoDepth | BW Sum | BW Avg | BW Max | BW Min | IOPS Sum | IOPS Avg | IOPS Max | IOPS Min | Avg Latency (ms) | 99th Percentile Latency (ms) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 9 | 1 | 9 | db-workload | randrw | 4k | 1 | 64 | 260 MiB/s | 29 MiB/s | ... | ... | 66.6k | ... | ... | ... | 9.74 | 36.96 |
Notes
bw_min/bw_max are per-client values, not bounds on the sum — do not use them as error bars on the aggregate
- Multiple rows per file are supported (e.g. a sweep over iodepth levels in the same run)
- The
uv run --with matplotlib flag adds matplotlib without modifying pyproject.toml
- The script reads files with suffix
"" (no extension) or .md — both formats are supported
- The
.png output is skipped automatically because .png is neither "" nor .md
- The PNG is always written to the same directory as the input files (the directory argument), not hardcoded to
results/
- Result files from
scp are typically named <short-uuid>-summary.md (e.g. d72df443-summary.md)
1---2name: plot-fio-results3description: Generate a 4-panel matplotlib chart from rally-ci-churn fio benchmark result files. Use when you have result files in the results/ directory and want to visualize throughput scaling, IOPS, latency (avg/P99), and scaling efficiency. Triggers: plot results, chart fio, visualize benchmark, fio chart, throughput chart.4---56# Plot FIO Benchmark Results78Generates a 4-panel PNG chart from fio benchmark markdown result files produced by `CIChurn.fio_distributed`.910## When to Use1112- After running one or more `fio_distributed` scenarios13- When results files exist in `results/` (or a custom directory)14- To compare throughput, IOPS, and latency across different client counts1516## Output1718- `<results_dir>/fio_benchmark_results.png` — 4-panel chart saved in the same directory as the input files19- Panel 1: Aggregate (sum) bandwidth per run, sorted by client count20- Panel 2: Aggregate IOPS per run21- Panel 3: Avg vs P99 latency per run (grouped bar)22- Panel 4: Actual bandwidth vs ideal linear scaling (saturation gap shaded)23- All panels: x-axis shows client count numbers only (e.g. `3`, `4`, `9`, `20`), labelled "FIO Client Count" — no filenames, no rotation2425## Procedure26271. **Locate result files** — accepts an optional directory argument (default: `results/` relative to workspace root); files may be in dated subdirectories (e.g. `results/apr7/`); each file is a markdown document with a `## Summary Table` section28292. **Parse each file** — extract the summary row using this regex pattern:30 ```31 ^\| (\d+)\s+\| (\d+)\s+\| (\d+)\s+\| (\S+)\s+\| (\S+)\s+\| (\S+)\s+\| (\d+)\s+\| (\d+)\s+32 \| ([\d.]+ \S+/s)\s+\| ([\d.]+ \S+/s)\s+\| ([\d.]+ \S+/s)\s+\| ([\d.]+ \S+/s)\s+33 \| ([\d.k]+)\s+\| ([\d.k]+)\s+\| ([\d.k]+)\s+\| ([\d.k]+)\s+\| ([\d.]+)\s+\| ([\d.]+)34 ```35 Fields: `clients, vols_per_client, total_vols, profile, rw, bs, numjobs, iodepth, bw_sum, bw_avg, bw_max, bw_min, iops_sum, iops_avg, iops_max, iops_min, avg_lat_ms, p99_lat_ms`36373. **Normalise bandwidth** — convert all BW values to MiB/s:38 - `GiB/s × 1024`, `MiB/s × 1`, `KiB/s ÷ 1024`39404. **Normalise IOPS** — strip trailing `k` and multiply by 100041425. **Sort rows** by `clients` ascending43446. **Run the plot script** — use `[./scripts/plot_fio_results.py](./scripts/plot_fio_results.py)` (already present in `scripts/`), passing the results directory as an argument:45 ```bash46 uv run --with matplotlib scripts/plot_fio_results.py <results_dir>47 ```48 e.g. `uv run --with matplotlib scripts/plot_fio_results.py results/apr7`49 > The script uses `matplotlib.use("Agg")` — no display required.50517. **Confirm output** — verify `<results_dir>/fio_benchmark_results.png` was saved in the same folder as the input files5253## Key Observations to Report5455After generating the chart, summarise:56- At what client count throughput/IOPS plateaued (storage saturation point)57- Latency degradation factor from lowest to highest client count58- Whether results are from a single profile or mixed (flag mixed profiles)5960## Result File Format Reference6162```markdown63## Summary Table6465| Client Nodes | Volumes/Client | Total Volumes | Profile | RW | Block Size | NumJobs | IoDepth | BW Sum | BW Avg | BW Max | BW Min | IOPS Sum | IOPS Avg | IOPS Max | IOPS Min | Avg Latency (ms) | 99th Percentile Latency (ms) |66|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|67| 9 | 1 | 9 | db-workload | randrw | 4k | 1 | 64 | 260 MiB/s | 29 MiB/s | ... | ... | 66.6k | ... | ... | ... | 9.74 | 36.96 |68```6970## Notes7172- `bw_min`/`bw_max` are per-client values, not bounds on the sum — do not use them as error bars on the aggregate73- Multiple rows per file are supported (e.g. a sweep over iodepth levels in the same run)74- The `uv run --with matplotlib` flag adds matplotlib without modifying `pyproject.toml`75- The script reads files with suffix `""` (no extension) **or** `.md` — both formats are supported76- The `.png` output is skipped automatically because `.png` is neither `""` nor `.md`77- The PNG is always written to the same directory as the input files (the directory argument), not hardcoded to `results/`78- Result files from `scp` are typically named `<short-uuid>-summary.md` (e.g. `d72df443-summary.md`)