Skip to content

Instantly share code, notes, and snippets.

@sonots
Last active August 29, 2015 14:06
Show Gist options
  • Save sonots/44b62561cc7f7a0007c6 to your computer and use it in GitHub Desktop.
Save sonots/44b62561cc7f7a0007c6 to your computer and use it in GitHub Desktop.
なんで gem install こんなに遅いん?

ruby 2.0.0p195

$ gem install chef-provider-service-daemontool -V
HEAD https://rubygems.org/latest_specs.4.8.gz
302 Moved Temporarily
HEAD https://s3.amazonaws.com/production.s3.rubygems.org/latest_specs.4.8.gz
304 Not Modified
ERROR:  Could not find a valid gem 'chef-provider-service-daemontool' (>= 0) in any repository
HEAD https://rubygems.org/prerelease_specs.4.8.gz
302 Moved Temporarily
HEAD https://s3.amazonaws.com/production.s3.rubygems.org/prerelease_specs.4.8.gz
304 Not Modified
HEAD https://rubygems.org/specs.4.8.gz
302 Moved Temporarily
HEAD https://s3.amazonaws.com/production.s3.rubygems.org/specs.4.8.gz
304 Not Modified
ここで12秒ぐらいかかる

strace してみる. syscall じゃわからなさそう

$ strace -f -s0 gem install chef-provider-service-daemontool -V
[pid 15338] brk(0xd31c000)              = 0xd31c000
[pid 15338] brk(0xd340000)              = 0xd340000
[pid 15338] brk(0xd364000)              = 0xd364000
[pid 15338] brk(0xd388000)              = 0xd388000
[pid 15338] brk(0xd3ac000)              = 0xd3ac000
[pid 15338] brk(0xd3d0000)              = 0xd3d0000
ここで12秒ぐらいかかる

tmm1 氏の rbtrace 仕込んでみる

$ gem install rbtrace
$ bundle exec which gem
/home/sonots/.rbenv/versions/haikanko/bin/gem
$ vim /home/sonots/.rbenv/versions/haikanko/bin/gem
+ require 'rbtrace'
$ gem install chef-provider-service-daemontool -V
$ rbtrace -p $(ps -ef | grep -v grep | grep gem | awk '{print $2}') --firehose | grep Gem
...
Gem::NameTuple#match_platform? <0.000938>
Gem::Text#levenshtein_distance
Gem::Text#levenshtein_distance <0.000195>
Gem::NameTuple#match_platform?
  Gem::Platform.match
    Gem.platforms <0.000085>
  Gem::Platform.match <0.000682>
Gem::NameTuple#match_platform? <0.000861>
Gem::Text#levenshtein_distance
Gem::Text#levenshtein_distance <0.000212>
Gem::NameTuple#match_platform?
  Gem::Platform.match
    Gem.platforms <0.000119>
  Gem::Platform.match <0.000861>
Gem::NameTuple#match_platform? <0.001166>
Gem::Text#levenshtein_distance
Gem::Text#levenshtein_distance <0.000271>
Gem::NameTuple#match_platform?
  Gem::Platform.match
    Gem.platforms <0.000081>
  Gem::Platform.match <0.000647>
Gem::NameTuple#match_platform? <0.000808>
Gem::Text#levenshtein_distance
Gem::Text#levenshtein_distance <0.000124>
Gem::NameTuple#match_platform?
  Gem::Platform.match
    Gem.platforms <0.000080>
  Gem::Platform.match <0.000582>
Gem::NameTuple#match_platform? <0.000713>
Gem::Text#levenshtein_distance
Gem::Text#levenshtein_distance <0.000128>
Gem::NameTuple#match_platform?
  Gem::Platform.match
以下ループ

ここかな https://github.com/ruby/ruby/blob/trunk/lib/rubygems/spec_fetcher.rb#L186-L206

@sonots
Copy link
Author

sonots commented Sep 25, 2014

From: /home/sonots/.rbenv/versions/haikanko/lib/ruby/2.0.0/rubygems/spec_fetcher.rb @ line 160 Gem::SpecFetcher#suggest_gems_from_name:

    154: def suggest_gems_from_name gem_name
    155:   gem_name        = gem_name.downcase.tr('_-', '')
    156:   max             = gem_name.size / 2
    157:   names           = available_specs(:complete).first.values.flatten(1)
    158:
    159:   require 'pry'
 => 160:   binding.pry
    161:   matches = names.map { |n|
    162:     next unless n.match_platform?
    163:
    164:     distance = levenshtein_distance gem_name, n.name.downcase.tr('_-', '')
    165:
    166:     next if distance >= max
    167:
    168:     return [n.name] if distance == 0
    169:
    170:     [n.name, distance]
    171:   }.compact
    172:
    173:   matches = matches.uniq.sort_by { |name, dist| dist }
    174:
    175:   matches.first(5).map { |name, dist| name }
    176: end

[2] pry(#<Gem::SpecFetcher>)> names.size
=> 497984

@sonots
Copy link
Author

sonots commented Sep 25, 2014

From: /home/sonots/.rbenv/versions/haikanko/lib/ruby/2.0.0/rubygems/spec_fetcher.rb @ line 201 Gem::SpecFetcher#available_specs:

    196:                   tuples_for source, :released
    197:                 when :complete
    198:     require 'pry'
    199:     binding.pry
    200:                   tuples_for(source, :prerelease, true) +
 => 201:                     tuples_for(source, :released)
    202:                 when :prerelease
    203:                   tuples_for(source, :prerelease)
    204:                 else
    205:                   raise Gem::Exception, "Unknown type - :#{type}"
    206:                 end

From: /home/sonots/.rbenv/versions/haikanko/lib/ruby/2.0.0/rubygems/spec_fetcher.rb @ line 220 Gem::SpecFetcher#tuples_for:

    217: def tuples_for(source, type, gracefully_ignore=false)
    218:   cache = @caches[type]
    219:
 => 220:   if gracefully_ignore
    221:     begin
    222:       cache[source.uri] ||= source.load_specs(type)
    223:     rescue Gem::RemoteFetcher::FetchError
    224:       []
    225:     end
    226:   else
    227:     cache[source.uri] ||= source.load_specs(type)
    228:   end
    229: end

[5] pry(#<Gem::SpecFetcher>)> source.load_specs(type)[0..3]
HEAD https://rubygems.org/prerelease_specs.4.8.gz
302 Moved Temporarily
HEAD https://s3.amazonaws.com/production.s3.rubygems.org/prerelease_specs.4.8.gz
304 Not Modified
=> [#<Gem::NameTuple:0x9d34c68 @name="3d_cache", @platform="ruby", @version=Gem::Version.new("0.0.01a")>,
 #<Gem::NameTuple:0x9d34c40 @name="3d_cache", @platform="ruby", @version=Gem::Version.new("0.0.02a")>,
 #<Gem::NameTuple:0x9d34c2c @name="3scale_client", @platform="ruby", @version=Gem::Version.new("2.4.0.pre.1")>,
 #<Gem::NameTuple:0x9d34c18 @name="99_game", @platform="ruby", @version=Gem::Version.new("1.0.3.pre")>]

@sonots
Copy link
Author

sonots commented Sep 25, 2014

typo してると全 gem 探索して一番近いやつを見つけてくれるらしい。それに 12s ほどかかる。

$ gem install chef-provider-service-daemontools -V

と正しくいれたらすぐ抜けた ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment