Opinionated Programmer - Jo Liss's musings on enlightened software development.

Profiling Spork for faster start-up time

Spork is a forking test runner for RSpec and Cucumber that allows you to run your tests almost instantaneously, without waiting for the entire Rails stack to load. If you haven’t checked it out, I strongly recommend you do so now (use 0.9.0.rc4 for Rails 3 support) – it’s awesome for iterative/test-first development.

For those who are using Spork already, here are two tricks to squeeze out the last few deciseconds from the start-up time:

Avoiding bundle exec

It’s much faster to run rspec --drb than bundle exec rspec --drb. Using bundle exec imposes a performance penalty of more than 1.5 seconds on my system. Just make sure that the rspec or cucumber binary on your system and the gem listed in your Gemfile.lock are the same version – then there should not be any need for bundle exec.

Profiling require

You can also add extra require statements to your Spork.preload block to force preloading. Here is a quick and dirty hack to find files that should be loaded at preload time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Spork.prefork do

  # ... your normal prefork block goes here ...

  module Kernel
    def require_with_trace(*args)
      start = Time.now.to_f
      @indent ||= 0
      @indent += 2
      require_without_trace(*args)
      @indent -= 2
      Kernel::puts "#{' '*@indent}#{((Time.now.to_f - start)*1000).to_i} #{args[0]}"
    end
    alias_method_chain :require, :trace
  end
end

Now restart spork, and run rspec --drb or cucumber --drb. It will print a bunch of modules that have been required after forking, prefixed by milliseconds. Indentation indicates indirect requires – read the list from bottom to top. The output should look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
3 spec_helper
3 action_view/test_case
3 spec_helper
3 action_view/test_case
    2 rspec/expectations/extensions/kernel
    4 rspec/expectations/extensions/array
  12 rspec/expectations/extensions
  3 rspec/matchers
  4 rspec/expectations/fail_with
  4 rspec/expectations/version
  4 rspec/expectations/backward_compatibility
        6 diff/lcs/change
      14 diff/lcs/callbacks
    23 diff/lcs
      7 diff/lcs/block
    15 diff/lcs/hunk
    4 pp
  52 rspec/expectations/differ
108 rspec/expectations
113 rspec/core/expecting/with_rspec
...

I suggest preloading any modules at the left-most indentation level that (a) do not belong to your project and (b) take more than 100 milliseconds. In my case, I’ve added the following block to my preload block:

1
2
3
4
5
6
7
8
9
  # Pre-loading for performance:
  require 'rspec/mocks'
  require 'rspec/expectations'
  require 'rspec/matchers'
  require 'active_support/secure_random'
  require 'selenium-webdriver'
  require 'selenium/webdriver/firefox/bridge'
  require 'rack/handler/webrick'
  require 'rexml/document'

Restart spork, and check that there are no left-over slow-loading modules. Then remove the tracing code. :-)

It’s a hack and only shaves off another 1.5 seconds on my system, but I think it’s worth it for something that I run hundreds of times a day.