Measure with Ruby-Prof

ruby-prof is a Ruby gem that comes with both an API and a command-line tool. In most cases you’ll probably want use the API to profile just that isolated part of the code that you suspect to be slow. But the command-line tool is what you want for profiling application startup, especially if you’re interested in rubygems startup costs. In Rails applications you can get the complete request profile by inserting ruby-prof into the middleware stack.

1. ruby-prof API

In this example we’ll use the ruby-prof API to profile a specific part of your code.

chp4/ruby_prof_example_api1.rb
 
require ​'date'
 
require ​'rubygems'
 
require ​'ruby-prof'
 
 
GC.disable
 
 
RubyProf.start
 
Date.parse(​"2014-07-01"​)
 
result = RubyProf.stop
 
 
printer = RubyProf::FlatPrinter.new(result)
 
printer.print(File.open(​"ruby_prof_example_api1_profile.txt"​, ​"w+"​))

Alternatively, you can pass a block to ruby-prof.

 
result = RubyProf.profile ​do
 
Date.parse(​"2014-07-01"​)
 
end

The idea is to wrap the code, in our example the Date#parse call, between RubyProf.start and RubyProf.stop and then print the report.

There are several types of reports ruby-prof can print, but for now we’ll use just one of them, called FlatPrinter, that shows the overall time spent in each function. The output is sorted by time, with functions that take the most time printed first, so you can easily see which functions are the CPU hogs.

Make sure you have the ruby-prof gem installed and run the program.

 
$ ​rbenv shell 2.1.5
 
$ ​gem install ruby-prof
 
$ ​ruby ruby_prof_example_api1.rb

Let’s briefly take a look at the profile.

chp4/ruby_prof_example_api1_profile.txt
 
Thread ID: 70237499659060
 
Fiber ID: 70237507374720
 
Total: 0.001111
 
Sort by: self_time
 
 
%self total self wait child calls name
 
59.98 0.001 0.001 0.000 0.000 2 Regexp#match
 
28.96 0.001 0.000 0.000 0.001 1 <Class::Date>#parse
 
4.72 0.001 0.000 0.000 0.001 1 Global#[No method]
 
3.81 0.000 0.000 0.000 0.000 1 String#gsub!
 
1.22 0.000 0.000 0.000 0.000 1 MatchData#begin
 
0.67 0.000 0.000 0.000 0.000 1 String#[]=
 
0.38 0.000 0.000 0.000 0.000 1 Fixnum#div
 
0.25 0.000 0.000 0.000 0.000 1 MatchData#end
 
 
* indicates recursively called methods

Our code spends more than 60% of its time matching the date I passed to the regular expression. Most of the remaining time is taken up by the Date#parse function itself, probably for Date instantiation.

Now let’s see how to use the command-line tool.

2. ruby-prof Command-Line Tool

You don’t have to add any instrumentation to the program itself in order to use the command-line tool. So the program can simply be this:

chp4/ruby_prof_example_command.rb
 
require ​'date'
 
 
GC.disable
 
Date.parse(​"2014-07-01"​)

Run the ruby-prof command to profile it.

 
$ ​ruby-prof -p flat -m 1 -f ruby_prof_example_command_profile.txt
 
ruby_prof_example_command.rb

Here the -p option tells which printer to use to output the report (flat printer again in our case). And -m limits the outputs by suppressing all functions that took less than a specified percentage of time to execute. In this case I don’t care about places where the code spends less than 1% of its time.

You should get a profile like this one:

chp4/ruby_prof_example_command_profile.txt
 
Thread ID: 69883126035220
 
Fiber ID: 69883132260680
 
Total: 0.002094
 
Sort by: self_time
 
 
%self total self wait child calls name
 
17.37 0.001 0.000 0.000 0.001 3 *Kernel#gem_original_requi
 
16.97 0.000 0.000 0.000 0.000 2 Regexp#match
 
10.85 0.001 0.000 0.000 0.000 1 <Class::Date>#parse
 
2.36 0.000 0.000 0.000 0.000 113 Module#method_added
 
1.83 0.000 0.000 0.000 0.000 6 IO#set_encoding
 
1.57 0.000 0.000 0.000 0.000 1 String#gsub!
 
1.23 0.000 0.000 0.000 0.000 6 MonitorMixin#mon_enter
 
1.16 0.002 0.000 0.000 0.002 2 Global#[No method]
 
1.12 0.000 0.000 0.000 0.000 3 Array#each
 
1.07 0.000 0.000 0.000 0.000 49 BasicObject#singleton_met
 
1.03 0.000 0.000 0.000 0.000 6 MonitorMixin#mon_exit
 
 
* indicates recursively called methods

With this profile you can see what took the most time during the whole program run. In this case it’s Kernel#gem_original_require—the line that we didn’t see when we profiled only date parsing. We can see the time spent in require ’date’. Other things like class and object initialization functions also appear in the profile. Unsurprisingly, our profile confirms that such a simple program spends more time on startup than on actual code execution.

Your profile may be a bit different, and sometimes GC#disable can appear at the top because it performs a lazy GC sweep before disabling. That may or may not take noticeable time depending on how many objects are currently marked for deletion in the memory heap. In case you’re wondering what that GC sweep is that I’m talking about, take a peek at Chapter 10, Tune Up the Garbage Collector. But don’t think too much about GC#disable if you spot it here, as we have it in the source code only for profiling.

As I mentioned, a profile generated by the ruby-prof command-line tool is very useful if you’re interested in the efficiency of application startup and gem loading. For other cases it adds too much data to the report, and you are much better off using the ruby-prof API.

3. Rails Middleware

The ruby-prof API works best if all you want is to profile a part of the Rails application—for example, controller action or template rendering. But to get insight into middleware, routing, and controller initialization, you need to insert ruby-prof as middleware.

To do that, add the ruby-prof gem into the Gemfile:

 
gem ​'ruby-prof'

Also, insert the ruby-prof Rack adapter into the middleware stack in, for example, config/applilcation.rb:

 
config.middleware.use Rack::RubyProf, path: ​'/tmp/rails_profile'

The use call will insert the profiler into the bottom of the middleware stack. So the profile will include Rails initialization code, skipping the middleware. If you want to profile the middleware, insert the profiler before Rack::Runtime:

 
config.middleware.insert_before Rack::Runtime, Rack::RubyProf,
 
path: ​'/tmp/rails_profile'

With Rack::RubyProf middleware loaded, you will get flat and HTML reports saved in the path you provide.

Don’t forget to disable GC before Rack::RubyProf starts. The best way to do it is to write one more middleware and insert it before the profiler into the stack:

 
class​ GCDisabler
 
def​ initialize(app)
 
@app = app
 
end
 
 
def​ call(env)
 
GC.start
 
GC.disable
 
response = @app.call(env)
 
GC.enable
 
response
 
end
 
end
 
 
# in config/application.rb
 
config.middleware.insert_before Rack::RubyProf, GCDisabler
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.107.100