Skip to content

perf: 5.9x speedup in procstat post-processing via metric caching - #36

Merged
atheurer merged 1 commit into
masterfrom
procstat-post-process-speedup
Jul 16, 2026
Merged

perf: 5.9x speedup in procstat post-processing via metric caching#36
atheurer merged 1 commit into
masterfrom
procstat-post-process-speedup

Conversation

@atheurer

Copy link
Copy Markdown
Contributor

Summary

Three targeted optimizations that together deliver a 5.9x wall-clock speedup (246s → 42s) for procstat-post-process.py on a representative 256-CPU dataset, validated with identical output.

  • cpu_topo_cache: memoize get_cpu_topology() per cpu_id. CPU topology is fixed for the lifetime of the process; it was previously looked up on every sample for every (irq, cpu) pair (~14M redundant calls on a 256-CPU system).

  • metric_idx_cache + log_sample_by_idx: on second and subsequent samples for a known (irq, cpu) pair, skip label computation and metric_idx lookup entirely by caching the CDMMetrics idx from the first log_sample() call. Uses the new log_sample_by_idx() fast path in the companion toolbox PR. Reduces warm-path work by ~800x.

  • Skip zero-value cold-path registrations: if ints_sec is zero on the first occurrence of an (irq, cpu) pair, do not register the metric. A metric whose first (and only) value is zero is always purged by finish_samples — registering it wastes ~99.9% of the work on a typical system where most IRQ/CPU combinations are idle. Metrics that become active after initially being zero are registered on their first non-zero sample, so active → zero → active transitions are still correctly captured.

Validation

Output was diff-validated against the original code on a 1/16th-size dataset:

  • Identical metric type sets (same IRQ/CPU combinations retained)
  • Identical non-zero metric values and row counts across all 4 parallel forks
  • The only difference: omission of leading zero-value segments before an IRQ's first active period, which the original code always purged anyway

Merge order

Merge the toolbox PR first: perftool-incubator/toolbox#126 — this PR depends on CDMMetrics.log_sample_by_idx() and log_sample() returning the metric idx.

Test plan

  • Merge toolbox PR #126 first
  • Run procstat-post-process.py on a dataset from a many-core host and verify output matches original
  • Confirm speedup on large datasets (256+ CPUs, long benchmark runs)

🤖 Generated with Claude Code

@k-rister k-rister left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic looks correct across all three optimizations:

  1. cpu_topo_cacheget_cpu_topology() is a pure dict lookup that returns the same result for a given cpu_id, so memoizing it is safe and eliminates millions of redundant calls at scale.

  2. metric_idx_cache + log_sample_by_idx — caching the idx from the first log_sample() call and using log_sample_by_idx() on subsequent calls skips label construction and dict lookup on the hot path. The cache key (irq, cpu) is correct since desc and the non-irq/cpu names fields are constant within process_interrupts.

  3. Skip zero-value cold-path — the elif ints_sec != 0 guard is the subtlest change but is correct: a metric whose first-ever sample is zero would always be purged by finish_samples (value==0 and no written samples → purge), so skipping registration avoids wasted work with identical output. The transition cases are handled correctly:

    • First zero, later non-zero: cache_key not in metric_idx_cache, falls through to elif ints_sec != 0, registers on first non-zero sample ✓
    • First non-zero, later zero: cache_key IS in cache, log_sample_by_idx called with value 0, metric correctly recorded ✓

Note: This PR depends on toolbox PR #126, which needs a rebase before it can merge — see my review there.

Three optimizations that together give a ~6x wall-clock speedup
on a representative procstat dataset (246s -> 42s):

1. cpu_topo_cache: memoize get_cpu_topology() per cpu_id. Topology
   is fixed for the lifetime of the process; previously looked up
   on every sample for every (irq, cpu) pair.

2. metric_idx_cache + log_sample_by_idx: on second and subsequent
   samples for a known (irq, cpu), skip label computation and
   metric_idx lookup entirely by caching the CDMMetrics idx returned
   by the first log_sample() call. Uses the new log_sample_by_idx()
   fast path added to CDMMetrics. Reduces warm-path log_sample work
   by ~800x on a 256-CPU system.

3. Skip zero-value cold-path registrations: if ints_sec is zero on
   the first occurrence of an (irq, cpu) pair, do not register the
   metric. A metric with zero as its first (and only) value is always
   purged by finish_samples anyway; registering it just to purge it
   wastes ~99.9% of the work on a typical system where most IRQ/CPU
   combinations are idle. Metrics that become active after being
   zero are registered on their first non-zero sample, so transitions
   are still correctly captured.

Validated: non-zero metric values and the full set of non-purged
metric types are identical between original and optimized output.
The only difference is omission of leading zero-value segments
before an IRQ's first active period, which were always purged.

Depends on toolbox log_sample_by_idx() change.
@atheurer
atheurer force-pushed the procstat-post-process-speedup branch from 7c30210 to 9d155e4 Compare July 15, 2026 23:33
@atheurer
atheurer requested a review from k-rister July 15, 2026 23:33
@atheurer
atheurer merged commit 941d023 into master Jul 16, 2026
37 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Crucible Tracking Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants