Skip to content

PR4/8: hwgraph cleanup#123

Open
ErwanAliasr1 wants to merge 11 commits into
mainfrom
hwgraph-output-polish
Open

PR4/8: hwgraph cleanup#123
ErwanAliasr1 wants to merge 11 commits into
mainfrom
hwgraph-output-polish

Conversation

@ErwanAliasr1

Copy link
Copy Markdown
Collaborator

This PR is cleaning up some rendering bugs and improves the overall rendering

ErwanAliasr1 and others added 11 commits July 15, 2026 16:46
The turbostat "starting/stopping background monitoring" messages were printed
for every single job, making the output very verbose and hard to read on runs
with many jobs. This information is still useful for debugging, so rather than
removing it, it is now gated behind a new --verbose (-v) flag (off by default).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
Pass --yaml to every stress-ng stressor so it writes its metrics next to
the stdout file (as <name>.yaml). We don't parse it yet, but capturing the
structured YAML output gives us a machine-readable source to build richer
parsing features on later, rather than scraping the human-oriented stdout.

Add a parsing fixture for stress-ng 0.21.03 under
hwbench/tests/parsing/stressng/v02103b/, including the real .yaml artifact
alongside stdout, so the auto-discovered parsing tests now cover this
version.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
Benchmark output files -- stdout, stderr, version-stdout, version-stderr
and, for stress-ng, the --yaml metrics file -- were named after the engine's
`name` (e.g. "stressngint128"). That name is shared by every run a single
job expands into: the scaling matrix (hosting_cpu_cores_scaling /
stressor_range) turns one job into many runs that all reuse it. As they
share one output directory, each iteration overwrote the previous one's
files, leaving only the last iteration on disk.

Introduce ExternalBench.output_basename, which prefixes the engine `name`
with get_name_with_position() -- the per-benchmark id already used as the
results.json key (and the expanded-job section header). _write_output and
the stress-ng --yaml file now use it, so files look like
"cpu10_5_stressngint128-stdout" / "....yaml" and map one-to-one to their
results.json entry. `name` keeps its original meaning; External falls back
to it for non-benchmark commands (External_Simple).

While here, fix a latent bug this path exposes: get_name_with_position()
guards on `if not self.benchmark`, but BenchmarkParameters never initialised
that attribute (only set_benchmark() did), so reading it before the owning
Benchmark is attached raised AttributeError instead of falling back to the
bare job name. Initialise self.benchmark = None.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
Add a per-NUMA-domain view of the per-core CPU metrics (frequency, IPC and
core power): instead of hundreds of unreadable per-core lines, plot one
averaged line per NUMA domain. Following the per-core convention, each metric
is rendered as "all_numa" (every core of each domain) and "pinned_numa" (only
the cores pinned during the job, grouped by their domain, dropping domains
with no pinned core; annotated with the pinned range).

This requires the NUMA topology in the trace, which hwbench collected but did
not serialize. cpu.dump() now writes:
  - numa_nodes: {domain: [logical cores]}
  - numa_distances: the inter-node distance matrix (kept for future
    topology-aware features)
Both are parsed from numactl -H; NUMA.get_distances() exposes the matrix that
was previously only used to derive quadrants. Traces predating this change
lack the topology and simply skip the NUMA graphs.

Tests reuse the existing NUMA fixtures: distance-matrix assertions in the
parse tests, a cpu.dump() topology test on the mocked hardware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
Complement the per-NUMA-domain line graphs with two heatmaps:

- A per-host NUMA distance heatmap (domain x domain, color = inter-domain
  distance), rendered once per trace in environment/by_host/<host>/, so the
  topology (which domains are close/far) is visible independently of any
  benchmark.

- A per-metric NUMA domain x time heatmap for frequency and IPC, next to the
  corresponding line graph in all_numa/pinned_numa: Y = NUMA domains, X = time
  (as in the line graphs), color = the domain's value at each step. A legend
  box on the left lists each domain's cores condensed via cpu_list_to_range()
  (individually bracketed, right-aligned, centered dash), like the component
  legend of the other graphs.

Graph.render() gains an extra_legend parameter so a manually placed legend is
included in the tight bounding box.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
IPC is usually interesting to see how cores are performing.
The frequency is not always enough to get a good estimation of the
core's performance, so let's report the IPC too.

Add an "ipc" SMP scaling graph, mirroring the cpu_clock one: add_perf() gains
ipc/ipc_err/ipc_cores parameters that aggregate the min/mean/max IPC across
cores (optionally restricted to the pinned cores), and smp_scaling renders an
ipc graph type with the same all_cores/pinned_cores variants and errorbars.

IPC is not always collected, so it is only aggregated and rendered when the
trace actually has an IPC context; IPC-less traces are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
Compare traces per NUMA domain across the scaling sweep. The first trace is
the reference; one heatmap is produced for each other trace, with the compared
trace name in the filename (scaling_<type>_numa_delta_<ref>_vs_<other>_<engine>).

Y = NUMA domains, X = scaling step (worker count), color = signed delta
value(reference) - value(other): red when the compared trace is lower than the
reference, green when higher, white when equal (diverging map centered at 0).
Rendered for frequency (cpu_clock) and IPC (cpu_ipc, when present), each in the
all_cores and (when the sweep pins cores) pinned_cores directories next to the
line graphs. On the pinned view, domains not pinned at a given step have no
data and are drawn black.

This will help understanding what NUMA mode was more or less active
during a step of the scaling.

A left box lists each domain's cores (condensed, via numa_core_blocks) and the
colour legend is a caption under the graph. Only produced when comparing at
least two traces exposing the NUMA topology.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
The lack of column and alignment in the header made it harder to read.

Before:
System: server_x_y_z Bios 3.2 Kernel 8.10.1

After:
System:server_x_y_z Bios:3.2 Kernel:8.10.1

Signed-off-by: Erwan Velu <e.velu@criteo.com>
With large data like cpu frequencies, the legend box tends to collide
with the Y-AXIS making it unreadable.

Let's move this box a bit away to the left to avid this.

Signed-off-by: Erwan Velu <e.velu@criteo.com>
The "No samples found in <metric>, ignoring metric." messages
polluted the output when a metric is missing.

A typical output was showing a missing PDU metric which can be a normal
thing.

So let's only print them if "--verbose" is requested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
The stats output printed one max line per power metric, including metrics
that had no data at all, e.g. "PDU max : 0.00 Watts in " with an empty bench
name. That 0.00 line looked like a buggy processing from hwgraph rather than
what it actually was: a simply missing metric.

Metrics are now only displayed if valid data is found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Erwan Velu <e.velu@criteo.com>
@ErwanAliasr1
ErwanAliasr1 force-pushed the hwgraph-output-polish branch from 6eea05a to 52224f1 Compare July 16, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants