Skip to content

feat: add rperf as optional flamegraph profiler backend#662

Open
sam-saffron-jarvis wants to merge 5 commits intoMiniProfiler:masterfrom
sam-saffron-jarvis:feat/rperf-flamegraph
Open

feat: add rperf as optional flamegraph profiler backend#662
sam-saffron-jarvis wants to merge 5 commits intoMiniProfiler:masterfrom
sam-saffron-jarvis:feat/rperf-flamegraph

Conversation

@sam-saffron-jarvis
Copy link
Copy Markdown

What

Adds rperf as an optional alternative to stackprof for generating flamegraphs.

New config option:

Rack::MiniProfiler.config.flamegraph_profiler = :rperf  # or :stackprof or :auto (default)

Or per-request via query param: ?pp=flamegraph&flamegraph_profiler=rperf

Why rperf?

rperf uses actual time deltas as sample weights to correct safepoint bias — a fundamental limitation of signal-based profilers like stackprof. Samples at safepoints can be delayed, making expensive-but-rare code appear cheap. rperf compensates by weighting each sample by the actual elapsed time since the previous sample.

In wall mode, rperf also emits synthetic frames for non-CPU time:

Frame Meaning
[GVL blocked] Off-GVL time (I/O, sleep, C extensions releasing GVL)
[GVL wait] Waiting to reacquire the GVL (contention)
[GC marking] Time in GC mark phase
[GC sweeping] Time in GC sweep phase

These appear as real frames in the flamegraph, making GVL contention and GC overhead directly visible without additional tooling.

Implementation

rperf's output (named frame pairs + pre-computed ns weights) maps naturally to speedscope's native sampled profile format, so no intermediate translation to StackProf format is needed. Speedscope auto-detects the format via $schema.

The existing StackProf path is completely unchanged.

Constraints

  • Requires gem 'rperf' + Ruby >= 3.4.0
  • Default :auto mode: prefers stackprof if loaded, falls back to rperf if only rperf is loaded
  • flamegraph_ignore_gc has no effect with rperf (GC frames always tracked)
  • flamegraph_sample_rate is reused as Hz for rperf (vs. ms interval for stackprof)

Adds rperf (https://github.com/ko1/rperf) as an optional alternative
to stackprof for generating flamegraphs in rack-mini-profiler.

## What

- New config option: `Rack::MiniProfiler.config.flamegraph_profiler`
  Values: `:auto` (default), `:stackprof`, `:rperf`
- New query param: `?pp=flamegraph&flamegraph_profiler=rperf`
- rperf output is converted to speedscope's native sampled profile
  format (rather than StackProf's format), since rperf's data maps
  directly to it (named frames, pre-computed ns weights).

## Why

rperf uses actual time deltas as sample weights to correct safepoint
bias — a fundamental limitation of signal-based profilers. In wall mode,
it also emits synthetic frames for [GVL blocked], [GVL wait], [GC
marking], and [GC sweeping], making GVL contention and GC overhead
directly visible in the flamegraph. This is not possible with stackprof.

## Constraints

- Requires rperf gem + Ruby >= 3.4.0
- `:auto` falls back to stackprof if rperf is not loaded, or rperf if
  stackprof is not loaded. Both present → prefers stackprof (existing
  behaviour unchanged).
- `flamegraph_ignore_gc` config has no effect with rperf (GC frames are
  always tracked, which is the better default).
- `flamegraph_sample_rate` config is reused but interpreted as Hz for
  rperf (vs. microsecond interval for stackprof). Defaults to 500 Hz.
@SamSaffron
Copy link
Copy Markdown
Member

SamSaffron commented Mar 24, 2026

@ko1 adding rperf to rack-mini-profiler as an option ... any concerns here?

(still need to carefully review PR and test... but it looks like an easy fit)

Jarvis and others added 4 commits March 24, 2026 12:35
…s nil store

- Update memcached service image: 1.6.9 → 1.6 (fixes Dalli 5.x meta protocol compat)
- Add Ruby 4.0 to CI matrix
- Change flush_tokens to use delete instead of set(nil) (nil value rejected by Dalli 5.x meta protocol)
@sam-saffron-jarvis
Copy link
Copy Markdown
Author

Screenshots — rperf flamegraph integration demo

Here's what the integration looks like running locally on the feat/rperf-flamegraph branch, using a minimal Puma/Rack app with rperf 0.5.0 installed and Rack::MiniProfiler.config.flamegraph_profiler = :rperf.

MiniProfiler toolbar (top-left badge)

toolbar

Expanded profiler view

expanded

Flamegraph output (?pp=flamegraph with rperf backend)

flamegraph

Flamegraph viewer (speedscope embed)

viewer


Setup used:

  • Ruby 3.4.8 on Linux x86_64
  • rperf 0.5.0 ✅ (available on RubyGems)
  • Rack::MiniProfiler.config.flamegraph_profiler = :rperf
  • Triggered via ?pp=flamegraph query param

Screenshots taken by Jarvis CI bot using Playwright/Chromium headless.

@ko1
Copy link
Copy Markdown

ko1 commented Mar 27, 2026

I have no opinion. Please note that rperf is very young project and I can't keep compatibility, at least this year.

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.

3 participants