Poor-man’s Benchmarking in Ruby

As part of evaluating several libraries for specification testing in Ruby (MSpec, RSpec, Bacon), I wanted to benchmark the performance of the library against a simple suite of tests to see if one was particularly slower than any of the others. It wasn’t intended to be very scientific but to at least expose a slow framework, if any.

Each benchmark was performed by creating a suite of specifications based around Bacon’s whirlwind sample (consisting of 5 specs), and executed the suite 10,000 times. This benchmark test was run 5 times in order to weed out any statistical anomolies. Nb: For this analysis, I didn’t benchmark RSpec because it’s not terribly compatible with IronRuby just yet.

The results and code can be found below. In a nutshell, it does seem as though MSpec performs slower than Bacon, but when you consider that over a 50,000 test sample it was only (roughly) 10 seconds slower, the difference is negligible.

  Bacon MSpec
Run 1 27.642 37.337
Run 2 25.598 37.755
Run 3 25.607 37.424
Run 4 25.317 36.439
Run 5 25.105 36.352
# This is the code for the spec test wrapper. 
# To execute, save the file as "spec_runner.rb" and execute
#    ruby spec_runner.rb
#
#

ITERATIONS = 10000

require 'rubygems'

@old_stdout = $stdout
$stdout = StringIO.new

def milestone(n)
$stdout = @old_stdout
puts ("Reached milestone: ##{n}")
$stdout = StringIO.new
	
end

def time_it(&func)
	start_time = Time.now
	1.upto(ITERATIONS) do |n|
		func.call
		milestone(n) if n % 1000 == 0
	end
	end_time = Time.now
	end_time-start_time
end

bacon_time = time_it do 
	load 'whirlwind_bacon.rb' 
end

mspec_time = time_it do 
	load 'whirlwind_mspec.rb' 
end

$stdout = @old_stdout
puts "bacon time: #{bacon_time}"
puts "mspec time: #{mspec_time}"
# This is the Bacon test file. Save it as "whirlwind_bacon.rb"
#
#

require 'bacon'

describe 'A new array' do
	before do
		@ary = Array.new
	end

	it 'should be empty' do
		@ary.should.be.empty
		@ary < < 1
		@ary.should.include 1
	end

	it 'should have zero size' do
		@ary.size.should.equal 0
		@ary.size.should.be.close 0.1, 0.5
	end

	it 'should raise on trying fetch any index' do
		lambda { @ary.fetch 0 }.
			should.raise(IndexError).
			message.should.match(/out of array/)
	end

	it 'should have an object identity' do
		@ary.should.not.be.same_as Array.new
	end

	palindrome = lambda { |obj| obj == obj.reverse }
	it 'should be a palindrome' do
		@ary.should.be.a palindrome
	end
end
# This is the MSpec test file. Save it as "whirlwind_mspec.rb"
#
#
require 'mspec'

describe 'A new array' do
	before do
		@ary = Array.new
	end

	it 'should be empty' do
		@ary.should be_empty
		@ary < < 1
		@ary.should include(1)
	end

	it 'should have zero size' do
		@ary.size.should.equal 0
		@ary.size.should be_close(0.1, 0.5)
	end

	it 'should raise on trying fetch any index' do
		d = lambda { @ary.fetch 0 }
		d.should raise_error(IndexError, /out of array/)
	end

	it 'should have an object identity' do
		@ary.should !equal(Array.new)
	end

	palindrome = lambda { |obj| obj == obj.reverse }
	it 'should be a palindrome' do
		(palindrome.call @ary).should be_true
	end
end

Leave a Reply

Your email address will not be published. Required fields are marked *