Update: If you're interested in profiling RSpec, you should take a look at this follow-up.
So I was happily coding away, until I managed to get myself sidetracked with the thought of profiling. Certainly, this is something that must be done for RTML, and it is quite high on the "things to do" list -- but it's not at the top. In any case, I was rather curious about how this could be done in rspec, so I set into a proof-of-concept which could be used to remind myself when the time came for some serious hard-core profiling action.
Turns out, rspec doesn't work all that well with ruby-prof. It does offer a commandline option that you can add to your spec/spec.opts file, however:
--format profile:spec/profile.txt
This will make a note of the 10 slowest specs, and save them to spec/profile.txt. Neat, but really not the sort of thing I'm looking for. I'll probably profile each spec individually at some point, so this information is redundant because I plan to do them all. To that end, I added some code to my spec/spec_helper.rb file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
require 'ruby-prof' #... Spec::Runner.configure do |config| if ENV['PROFILE'] config.before(:all) do unless $started $started = 1 puts "start profiler for #{described_class}" RubyProf.start end end config.after(:all) do if $started puts "stop profiler for #{described_class}" $started = nil FileUtils.mkdir_p "profiled_specs" file = "profiled_specs/#{described_class.to_s.underscore}.html" result = RubyProf.stop printer = RubyProf::GraphHtmlPrinter.new(result) printer.print(file, :min_percent => (ENV['MIN_PERCENT'] || 0.1).to_f) end end end end |
What this code does is simply trigger RubyProf at the start of each example, and construct a result at the end. I found that this also fires for every context, so you'll probably need to construct the profile out of the whole description, which you can get by calling #name. In any case, it's a starting point.
However, I still needed to profile all of the specs collectively. After dinking around with this for about 45 minutes, I came to the conclusion that there's no ideal way to do this from within rspec. However, I was overlooking one very important option: the ruby-prof command. RubyProf provides a command line executable that you can use to invoke it. I couldn't call my specs directly, but I could have RubyProf profile a single file, which would then bootstrap the rest of the framework. Here're the contents of that file:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
if ENV["_"] =~ /ruby\-prof/ Dir[File.join(File.dirname(__FILE__), 'spec/**/*_spec.rb')].each do |fi| next unless File.file? fi load fi end else puts <<-end_banner This script will use ruby-prof to run the specs. Usage: ruby-prof do_profile.rb end_banner end |
This basically just verifies that it was invoked using "ruby-prof" and not plain old "ruby"; if it was invoked incorrectly then the user probably didn't know or didn't remember (nudging myself here) what the script was for, so it dumps a usage banner out.
That's what I've discovered for tonight, and I'm confident that it'll give me most of what I need when the proper time for profiling rolls around. Hopefully it'll help someone else out there, too.


