ruby-changes:62445
From: Jean <ko1@a...>
Date: Fri, 31 Jul 2020 21:08:11 +0900 (JST)
Subject: [ruby-changes:62445] 76722c4928 (master): [rubygems/rubygems] Optimize Gem.already_loaded?
https://git.ruby-lang.org/ruby.git/commit/?id=76722c4928 From 76722c4928d92fa6cc4a927203eab8a036516e23 Mon Sep 17 00:00:00 2001 From: Jean Boussier <jean.boussier@g...> Date: Mon, 6 Jul 2020 15:18:05 +0200 Subject: [rubygems/rubygems] Optimize Gem.already_loaded? Profiling a simple `ruby -e '1'` I see: ``` ================================== Mode: wall(10) Samples: 3414 (55.10% miss rate) GC: 856 (25.07%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 689 (20.2%) 669 (19.6%) Gem.already_loaded? 462 (13.5%) 462 (13.5%) (marking) 393 (11.5%) 393 (11.5%) (sweeping) 815 (23.9%) 365 (10.7%) Gem::Specification.load 1050 (30.8%) 156 (4.6%) Gem.register_default_spec 100 (2.9%) 84 (2.5%) Gem::Specification#files 64 (1.9%) 64 (1.9%) Gem::BasicSpecification#internal_init 136 (4.0%) 59 (1.7%) <top (required)> 2312 (67.7%) 58 (1.7%) <top (required)> 57 (1.7%) 57 (1.7%) RbConfig.expand 81 (2.4%) 55 (1.6%) Gem::Requirement.parse 191 (5.6%) 51 (1.5%) <top (required)> 128 (3.7%) 47 (1.4%) Gem::Requirement#initialize 41 (1.2%) 41 (1.2%) Gem.suffix_regexp 229 (6.7%) 35 (1.0%) <module:Gem> 260 (7.6%) 34 (1.0%) Kernel#require ``` So clearly `Gem.already_loaded?` is a major hotspot. After this optimization, it's down to: ``` ================================== Mode: wall(10) Samples: 2653 (58.21% miss rate) GC: 718 (27.06%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 416 (15.7%) 416 (15.7%) (marking) 715 (27.0%) 312 (11.8%) Gem::Specification.load 299 (11.3%) 299 (11.3%) (sweeping) 279 (10.5%) 279 (10.5%) Gem.already_loaded? 564 (21.3%) 106 (4.0%) Gem.register_default_spec 95 (3.6%) 80 (3.0%) Gem::Specification#files 72 (2.7%) 72 (2.7%) Gem::BasicSpecification#internal_init 129 (4.9%) 58 (2.2%) <top (required)> 53 (2.0%) 53 (2.0%) RbConfig.expand 1697 (64.0%) 52 (2.0%) <top (required)> 68 (2.6%) 49 (1.8%) Gem::Requirement.parse 183 (6.9%) 48 (1.8%) <top (required)> 112 (4.2%) 44 (1.7%) Gem::Requirement#initialize 220 (8.3%) 33 (1.2%) <module:Gem> 250 (9.4%) 32 (1.2%) Kernel#require ``` The idea is that the vast majority of the time `already_loaded?` won't match anything. So by first looking for candidate paths that `end_with?` the file we look for, we save `default_gem_load_paths.size` iterations and string concatenations. https://github.com/rubygems/rubygems/commit/c60ce88d49 diff --git a/lib/rubygems.rb b/lib/rubygems.rb index e72d62b..8e23c32 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -1300,8 +1300,8 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} https://github.com/ruby/ruby/blob/trunk/lib/rubygems.rb#L1300 private def already_loaded?(file) - default_gem_load_paths.find do |load_path_entry| - $LOADED_FEATURES.include?("#{load_path_entry}/#{file}") + $LOADED_FEATURES.any? do |feature_path| + feature_path.end_with?(file) && default_gem_load_paths.any? {|load_path_entry| feature_path == "#{load_path_entry}/#{file}" } end end -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/