bundler/bundler — pull request #3113 by dubek (2014-07-25 03:15:38+00:00)
Title: Performance: implement #hash for Source classes

Correctly implement `#hash` for `Source::Path`, `Source::Git` and `Source::SVN`, thus improve performance when loading a Gemfile with a lot of sources. The `#hash` method is used when gathering sources in a `Set` or when calling `Array#uniq` or `Array#|` (union).

By correctly implementing `#hash` we save unneeded calls to `#eql?` and therefore improve loading times of gems when many sources are listed in Gemfile.

## How to test

Run this script in an empty directory to create a project with 200 dummy local libraries:

```
#!/bin/bash

# Create 200 dummy (empty) Ruby libraries under the current directory
cat < prepare_libs.rb
require 'fileutils'
(1..200).each { |i|
FileUtils.mkdir_p("mylib#{i}/lib")
File.open("mylib#{i}/mylib#{i}-1.0.gemspec", "w") { |f|
f.puts <<-EOS
Gem::Specification.new do |s|
s.name = "mylib#{i}"
s.version = "1.0"
end
EOS
}
File.open("mylib#{i}/lib/mylib#{i}.rb", "w") { |f|
f.puts "# dummy"
}
}
EOF

ruby prepare_libs.rb

# Build a Gemfile that loads all those local libraries using :path source and prints timings
cat < Gemfile
require 'benchmark'

load_times = (1..200).map do |i|
Benchmark.realtime {
gem "mylib#{i}", :path => "mylib#{i}", :require => false
}
end

STDERR.puts "first gem load took #{load_times.first} seconds"
STDERR.puts "last gem load took #{load_times.last} seconds"
STDERR.puts "last is #{load_times.last / load_times.first} times slower than first"
EOF

# Load Gemfile and print timings
bundle exec ruby -v
```

## Benchmarks

### Old

Performance with old version (v1.6.5) - loading 200 gems:

```
$ bundle exec ruby -v
first gem load took 0.002427815 seconds
last gem load took 0.142106602 seconds
last is 58.53271439545435 times slower than first
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]
```

### New

Performance improvement with fix:

```
$ dbundle exec ruby -v
first gem load took 0.001243233 seconds
last gem load took 0.001156801 seconds
last is 0.9304780358951218 times slower than first
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]
```

Now loading the 200th gem takes the same time as loading the first gem.