Support profiling with vernier - #79
Merged
Merged
Conversation
Singed is about to support profiling with vernier as an optional dependency, so its specs and Sorbet need the gem. The RBI is generated with `bin/tapioca gems`.
Closes #26. Vernier samples each thread separately, so a flamegraph from a multi-threaded app isn't a mix of what all its threads were doing. Pass `profiler: :vernier` to `flamegraph`, `Singed.start` or a controller's `flamegraph`, or set `Singed.profiler = :vernier` to make it the default for every integration. stackprof stays the default. Vernier isn't a dependency of the gem: it's a native extension that only some apps want, so Singed requires it when asked to use it and explains what's missing if it can't. Singed::VernierReport converts Vernier's result to speedscope's file format, with a profile per thread, so flamegraphs still open in the bundled speedscope. Samples from threads that were waiting get an `(idle)` or `(waiting for GVL)` frame on top, rather than looking like time spent in the code that started waiting.
dduugg
force-pushed
the
add-vernier-profiler
branch
from
September 29, 2026 21:31
c00fa65 to
dd2d2dc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #26.
Vernier profiles each thread separately, so a flamegraph from a multi-threaded app like Puma or Sidekiq isn't a mix of what all its threads were doing. This adds it as a second profiler alongside stackprof, which stays the default.
Usage
How it works
LoadErrorsaying to add it. If vernier is installed but fails to load, such as a native extension built for another Ruby, the original error comes through unchanged. It also raises if the loaded vernier is older than 1.5, since the gemspec can't pin an optional gem's version.Singed.profiler =does all of this too, so a missing or outdated gem, or a typo, fails at boot rather than on the first profiled request. The README says to set it only where vernier is bundled.cpuprofileoutput only includes the main thread. So the newSinged::VernierReportconverts Vernier's result into speedscope's file format itself:n/p. Threads that recorded no samples are left out.(idle)or(waiting for GVL)frame on top, so time spent waiting doesn't look like time spent running the code that started waiting.Singed.filter_line, the same as stackprof and rbspy output. Function, file and thread names are made valid UTF-8 first. Vernier doesn't check its strings, and it names unnamed threads afterThread#inspect, which is binary; either kind would otherwise makeJSON.generateraise or warn.Vernier::Collector, rather than usingVernier.start_profile, which raises if a profile is already running.ignore_gcdoes nothing with Vernier, because Vernier doesn't sample a thread while it's running GC. The README says so.flamegraph.profileis the rawVernier::Result, as it's the rawStackProf.resultsfor stackprof. That means the same recording can also be saved for vernier.prof, which shows GVL and GC activity.Vernier::*constant. Tapioca evaluates them in apps that may not load vernier, soprofileandstopbecomeuntyped.#29 tried this before, opening Vernier's Firefox-format output with
profile-viewer. This PR keeps speedscope as the one viewer so that everything Singed already does, including the bundled offline speedscope, works unchanged.Not in this PR
singedcommand line still records with rbspy.Testing
bundle exec rspecpasses on Ruby 3.3.11, 3.4.11 and 4.0.5, with vernier 1.11.0 compiling on each.srb tcandrubocopare clean.VernierReportspecs use verifying doubles ofVernier::StackTableandVernier::Result, so the data is deterministic and the method names are still checked against Vernier.FlamegraphandSinged.start/stopspecs record with real Vernier, including a second thread.(idle)underKernel#sleepand(waiting for GVL)while another thread held the GVL.savetook 0.06 s and wrote 1.6 MB. stackprof wrote 6.5 MB for the same work.is_start,meta[:interval]and theStackTableaccessors date from 1.1, but the publicVernier::Result#stack_tablereader arrived in 1.5.