Measuring Rails Boot Time

Our main rails app is pretty slow to load, so I hacked together a really simple report to show what’s going on during boot. This system could use a lot of improvement to aggregate together requires, but it’s enough to draw some immediate conclusions.

First, I created this script that overrides Kernel.require:

Kernel.require 'singleton'
class BootMeasurement
  include Singleton
  def initialize
    @req_starts = {}
    @req_times = {}
  end

  def self.start(requirement)
    instance.start(requirement.to_s)
  end
  def self.finish(requirement)
    instance.finish(requirement.to_s)
  end

  def start(req)
    @first ||= Time.now
    @req_starts[req] ||= []
    @req_starts[req] << Time.now
  end
  def finish(req)
    raise "Requirement #{req} unstarted!" unless @req_starts.include? req
    @req_times[req] ||= []
    @req_times[req] << Time.now - @req_starts[req].last
    @last = Time.now
  end

  def self.report
    instance.report
  end

  def self.fmt(time)
    "#{time.round(2)} sec"
  end

  def report
    return unless @req_times.any?
    lng = @req_times.keys.max{|a,b| a.length <=> b.length }.length
    @req_times.map do |req, times|
      ttime = times.inject(0){|s, e| s + e }
      [req, ttime]
    end.sort{|a,b| a[1] <=> b[1] }.each do |req, ttime|
      puts "#{req.ljust(lng, ' ')}: #{BootMeasurement.fmt(ttime)}"
    end
    puts "Total time spent requiring: #{BootMeasurement.fmt(@last - @first)}"
  end
end

alias kernel_base_require require
def require(file)
  BootMeasurement.start(file)
  kernel_base_require(file)
  BootMeasurement.finish(file)
end

Then in boot.rb, I require it:

require File.join(File.dirname(__FILE__), '..', 'require_timer')
require 'rubygems'

# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)

require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])

Then I run this simple runner script:

BootMeasurement.report
puts "Done"

And I see something like

user@host:~/src/rails_app (develop) $ time bundle exec rails r booted.rb
( lots of lines elided... )
exception_notifier/notifier                    : 1.37 sec
yard-cucumber                                  : 1.63 sec
net/ssh                                        : 2.07 sec
net/ssh/gateway                                : 2.09 sec
capistrano/configuration/connections           : 2.17 sec
capistrano/configuration                       : 3.25 sec
/home/user/src/rails_app/config/environment.rb : 14.24 sec
/home/user/src/rails_app/config/application    : 29.57 sec
Total time spent requiring: 43.82 sec
Done

real 0m48.486s
user 0m46.247s
sys  0m1.344s

Not perfect, but gives me some insight!

comments powered by Disqus