require 'ruby-prof' require 'benchmark' module BenchmarkHelpers PROFILE_OUTPUT_DIR = "profiling" def self.safe_filename(name) name.gsub(" ", "_") end def self.example_output_target_file!(example) dir = File.join([ PROFILE_OUTPUT_DIR ] + example.example_group.ancestors.map { |a| BenchmarkHelpers.safe_filename(a.description) }) FileUtils.mkdir_p(dir) File.expand_path(BenchmarkHelpers.safe_filename(example.description), dir) end def self.profile_example(example, options = {}, &block) # kcachegrind output default options = { profile_printer: RubyProf::CallTreePrinter }.merge(options) RubyProf.start begin example.example_group_instance.instance_eval(&block) ensure result = RubyProf.stop end printer = options[:profile_printer].new(result) filename = BenchmarkHelpers.example_output_target_file!(example) file = File.new(filename + ".dat", 'w') printer.print(file, {}) end def self.benchmark_example(example, options = {}, &block) Benchmark.bm(options[:label_width] || example.description.length) do |bm| bm.report(example.description) do example.example_group_instance.instance_eval(&block) end end end module ExampleGroupMethods def profile(description, options = {}, &block) benchmark_profile(description, { profile: true }.merge(options), &block) end def benchmark(description, options = {}, &block) benchmark_profile(description, { benchmark: true }.merge(options), &block) end def benchmark_profile(description, options, &block) proc = block if options[:benchmark] proc = Proc.new { BenchmarkHelpers.benchmark_example(example, options, &block) } end if options[:profile] inner_proc = proc proc = Proc.new { BenchmarkHelpers.profile_example(example, options, &inner_proc) } end it description, options, &proc end end end RSpec.configure do |c| c.color_enabled = true c.extend BenchmarkHelpers::ExampleGroupMethods end require 'spec_helper' describe "benchmarks" do def calc; (0..100).inject {|sum, n| sum + n } end it "should calculate something" do calc.should == 5050 end benchmark "should benchmark" do calc.should == 5050 end benchmark "should benchmark and profile", profile: true do calc.should == 5050 end benchmark "should only profile", benchmark: false, profile: true do calc.should == 5050 end profile "should only profile #2" do calc.should == 5050 end end [you@computer project]$ rake spec SPEC=spec/benchmark_spec.rb /home/you/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -S bundle exec rspec spec/benchmark_spec.rb benchmarks should calculate something user system total real should benchmark 0.000000 0.000000 0.000000 ( 0.000102) should benchmark user system total real should benchmark and profile 0.000000 0.000000 0.000000 ( 0.000208) should benchmark and profile should only profile should only profile #2 Finished in 0.01528 seconds 4 examples, 0 failures