Pilfer
Profile Ruby code and find out exactly how slow it runs.
Pilfer uses rblineprof to measure how long each line of code takes to execute and the number of times it was called.
Take a look at some Pilfer profiles of the Bundler API site.
Installation
Using with Bundler is as simple as adding pilfer
to your Gemfile
.
gem 'pilfer', '~> 1.0.0'
Or install it locally like any other gem.
$ gem install pilfer
Usage
Profile a block of code saving the report to the file profile.log
.
require 'pilfer'
reporter = Pilfer::Logger.new('pilfer.log')
profiler = Pilfer::Profiler.new(reporter)
profiler.profile('bubble sorting') do
array = (0..100).to_a.shuffle
bubble_sort array
end
Profile your Rack or Rails app using Pilfer::Middleware
.
reporter = Pilfer::Logger.new('pilfer.log')
profiler = Pilfer::Profiler.new(reporter)
use Pilfer::Middleware, :profiler => profiler
The profile report consists of the wall time and call count for each line of code executed along with the total wall and CPU times for each file.
Profile start="2013-05-12 00:41:16 UTC" description="bubble sorting"
/Users/Larry/Sites/pilfer/sort.rb wall_time=42.5ms cpu_time=29.7ms
| require 'pilfer'
|
| def bubble_sort(container)
42.5ms ( 1) | loop do
| swapped = false
42.3ms ( 94) | (container.size-1).times do |i|
10.2ms ( 9400) | if (container[i] <=> container[i+1]) == 1
6.2ms ( 5092) | container[i], container[i+1] = container[i+1], container[i] # Swap
| swapped = true
| end
| end
| break unless swapped
| end
| container
| end
|
| reporter = Pilfer::Logger.new($stdout)
| profiler = Pilfer::Profiler.new(reporter)
| profiler.profile_files_matching(/sort\.rb/, 'bubble sorting') do
0.1ms ( 3) | array = (0..100).to_a.shuffle
42.5ms ( 1) | bubble_sort array
| end
Step 1: Create a reporter
Decide how you want line profiles to be reported. Profiles can be sent to a
pilfer-server or written to a file path or IO
object.
# Send reports to a pilfer-server
reporter = Pilfer::Server.new('https://pilfer.com', 'my-pilfer-server-token')
# Append reports to a file
reporter = Pilfer::Logger.new('pilfer.log')
# Print reports to standard out
reporter = Pilfer::Logger.new($stdout)
The absolute path to each profiled file is used in the report. Set the path to
the application root with :app_root
to have it trimmed from reported file
paths.
reporter = Pilfer::Logger.new('pilfer.log')
# Profile start=2013-05-02 14:17:26 UTC
# /Sites/bundler-api/lib/bundler-api/web.rb wall_time=1009.5ms cpu_time=0.5ms
# ...
reporter = Pilfer::Logger.new('pilfer.log', :app_root => '/Sites/bundler-api/')
# Profile start=2013-05-02 14:17:26 UTC
# lib/bundler-api/web.rb wall_time=1009.5ms cpu_time=0.5ms
# ...
Step 2: Create a profiler
A Profiler
runs the line profiler and sends it to a reporter. Create one
passing the reporter created in the previous step.
profiler = Pilfer::Profiler.new(reporter)
Step 3: Profile a block of code
Use Profiler#profile
to profile a block of code. Optionally, provide a
description of the code being profiling.
profiler.profile('bubble sorting') do
array = (0..100).to_a.shuffle
bubble_sort array
end
Every file that's executed by the block including code outside the
application like gems and standard libraries will be included in the profile.
Use #profile_files_matching
to limit profiling to files whose paths match a
regular expression.
# Only profile Rails models
matcher = %r{^#{Regexp.escape(Rails.root.to_s)}/app/models}
profiler.profile_files_matching(matcher, 'User.find_by_email') do
User.find_by_email('arthur@dent.com')
end
Additional arguments to #profile
and #profile_files_matching
will be
passed to the reporter. The Pilfer::Server
reporter, for example, can submit
profiles asynchronously.
profiler.profile('bubble sorting', :submit => :async) do
array = (0..100).to_a.shuffle
bubble_sort array
end
Extras
Pilfer Server
Pilfer Server is your own, personal service for collecting and viewing line profiles gathered by Pilfer. Follow the Pilfer Server setup instructions to stand up a new server.
reporter = Pilfer::Server.new('https://pilfer.com', 'my-pilfer-server-token')
profiler = Pilfer::Profiler.new(reporter)
profiler.profile('bubble sorting') do
array = (0..100).to_a.shuffle
bubble_sort array
end
Rack Middleware
Profile your entire Rack or Rails app using Pilfer::Middleware
. Pass it a
Profiler
created with a reporter as normal.
reporter = Pilfer::Server.new('https://pilfer.com', 'my-pilfer-server-token')
profiler = Pilfer::Profiler.new(reporter)
use Pilfer::Middleware, :profiler => profiler
Restrict profiling to files matching a regular expression using the
:files_matching
option. This calls Profiler#profile_files_matching
using
the given regular expression.
matcher = %r{^#{Regexp.escape(Rails.root.to_s)}/(app|config|lib|vendor/plugin)}
use Pilfer::Middleware, :profiler => profiler,
:file_matcher => matcher
You almost certainly don't want to profile every request. Provide a block to determine if a profile should be run on the incoming request.
use Pilfer::Middleware, :profiler => profiler do
# Profile 1% of requests.
rand(100) == 1
end
The Rack environment is available to allow profiling on demand.
# Profile requests containing the query string ?profile=true
use Pilfer::Middleware, :profiler => profiler do |env|
env["QUERY_STRING"].include? 'profile=true'
end
# Profile requests containing a header whose value matches a secret
use Pilfer::Middleware, :profiler => profiler do |env|
env['HTTP_PROFILE_AUTHORIZATION'] == 'super-secret'
end
Supported Ruby Versions
This library is tested against the following Ruby versions.
- MRI 1.9.3
- MRI 1.8.7
- REE
If you need a specific version supported, open and issue or send a pull request.
License
The MIT License (MIT)
Copyright (c) 2013 Eric Lindvall and Larry Marburger. See LICENSE for details.