Singed makes it easy to get a flamegraph anywhere in your code base. It wraps profiling your code with stackprof or rbspy, and then launching speedscope to view it.
But why would you want to do this? At least a couple of reasons:
- you have code that you either feel, think, or KNOW is slow
- you are curious about how some code actually runs
Add to Gemfile:
gem "singed"Then run bundle install
Then run npm install -g speedscope
The simplest way is to wrap your code with a block
flamegraph {
# your code here
}Flamegraphs are saved for later review to Singed.output_directory, which is tmp/speedscope on Rails. You can adjust this like:
Singed.output_directory = "tmp/slowness-exploration"If you are calling it in a loop, or with different variations, you can include a label on the filename:
flamegraph("rspec") {
# your code here
}You can also skip opening speedscope in a browser automatically:
flamegraph(open: false) {
# your code here
}flamegraph takes some of the options that stackprof does:
ignore_gc: true: if your profiled code is very memory heavy, the garbage collection can make it harder to read. ignoring gc can make it more readable, but it's also a sign to use memory_profiler.interval: 1000(in ms): how frequently to sample. for very small
Plus some of its own:
label: "your-description": a label to include in the filenameopen: false: don't try toopenthe flamegraph in a browserio: File.open("your-output")where to write the output, defaults to$stdout
If you are using RSpec, you can use the flamegraph metadata to capture it for you. This RSpec::Core::Hooks#around, so focuses only on code inside the example block.
# make sure this is required at somepoint, like in a spec/support file!
require 'singed/rspec'
RSpec.describe YourClass do
it "is slow :(", flamegraph: true do
# your code here
end
endIf you want to capture a flamegraph of a controller action, you can call it like:
class EmployeesController < ApplicationController
flamegraph :show
def show
# your code here
end
endThis won't catch the entire request though, just once it's been routed to controller and a response has been served (ie no middleware).
To capture the whole request, there is a middleware which checks for the X-Singed header to be 'true'. With curl, you can do this like:
curl -H 'X-Singed: true' https://localhost:3000PROTIP: use Chrome Developer Tools to record network activity, and copy requests as a curl command. Add -H 'X-Singed: true' to it, and you get flamegraphs!
This can also be enabled to always run by setting SINGED_MIDDLEWARE_ALWAYS_CAPTURE=1 in the environment.
There is a singed command line you can use that will record a flamegraph from the entirety of a command run:
$ bundle binstub singed # if you want to be able to call it like bin/singed
$ bundle exec singed -- bin/rails runner 'Model.all.to_a'The flamegraph is opened afterwards.
You've generated a flamegraph, now what? That's a great question. Here's some resources for getting started:
- technicalpickles/flamegraph-lighting-talk
- Pairin' with Aaron: Flamegraphs and App Performance - YouTube
When using the auto-opening feature, it's assumed that you are have a browser available on the same host you are profiling code.
The open is expected to be available.