diff --git a/CHANGELOG.md b/CHANGELOG.md index 3216455dc924..ecfc4210c569 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Compatibility: Performance: +* Improve `Truffle::FeatureLoader.loaded_feature_path` by removing expensive string ops from a loop. Speeds up feature lookup time (#3010, @itarato). Changes: diff --git a/spec/truffle/kernel/feature_loader_spec.rb b/spec/truffle/kernel/feature_loader_spec.rb index 5581281d77e7..df890df2c71a 100644 --- a/spec/truffle/kernel/feature_loader_spec.rb +++ b/spec/truffle/kernel/feature_loader_spec.rb @@ -53,27 +53,27 @@ end end -describe "Truffle::FeatureLoader.loaded_feature_path" do +describe "Truffle::FeatureLoader.feature_path_loaded?" do it "returns path for matching feature" do load_path = ["/path/ruby/lib/ruby/2.6.0"] loaded_feature = "/path/ruby/lib/ruby/2.6.0/benchmark.rb" feature = "benchmark" - path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path) - path.should == load_path[0] + path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path) + path.should be_true feature = "benchmark.rb" - path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path) - path.should == load_path[0] + path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path) + path.should be_true end - it "returns nil for missing features" do + it "returns false for missing features" do load_path = ["/path/ruby/lib/ruby/2.6.0"] - path = Truffle::FeatureLoader.loaded_feature_path("/path/ruby/lib/ruby/2.6.0/benchmark.rb", "missing", load_path) - path.should == nil + path = Truffle::FeatureLoader.feature_path_loaded?("/path/ruby/lib/ruby/2.6.0/benchmark.rb", "missing", load_path) + path.should be_false long_feature = "/path/ruby/lib/ruby/2.6.0/extra-path/benchmark.rb" - path = Truffle::FeatureLoader.loaded_feature_path("/path/ruby/lib/ruby/2.6.0/benchmark.rb", long_feature, load_path) - path.should == nil + path = Truffle::FeatureLoader.feature_path_loaded?("/path/ruby/lib/ruby/2.6.0/benchmark.rb", long_feature, load_path) + path.should be_false end it "returns correct paths for non-rb paths" do @@ -81,16 +81,16 @@ loaded_feature = "/path/ruby/lib/ruby/2.6.0/benchmark.so" feature = "benchmark" - path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path) - path.should == load_path[0] + path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path) + path.should be_true feature = "benchmark.so" - path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path) - path.should == load_path[0] + path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path) + path.should be_true feature = "benchmark.rb" - path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path) - path.should == nil + path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path) + path.should be_false end end diff --git a/src/main/ruby/truffleruby/core/truffle/feature_loader.rb b/src/main/ruby/truffleruby/core/truffle/feature_loader.rb index bcc0a6aff702..8ecdbd8cacbc 100644 --- a/src/main/ruby/truffleruby/core/truffle/feature_loader.rb +++ b/src/main/ruby/truffleruby/core/truffle/feature_loader.rb @@ -165,16 +165,16 @@ def self.feature_provided?(feature, expanded) loaded_feature = $LOADED_FEATURES[fe.index] next if loaded_feature.size < feature.size - feature_path = if loaded_feature.start_with?(feature) - feature - else - if expanded - nil - else - loaded_feature_path(loaded_feature, feature, get_expanded_load_path) - end - end - if feature_path + found_feature_path = if loaded_feature.start_with?(feature) + true + else + if expanded + false + else + feature_path_loaded?(loaded_feature, feature, get_expanded_load_path) + end + end + if found_feature_path loaded_feature_ext = extension_symbol(loaded_feature) if !loaded_feature_ext return :unknown unless feature_ext @@ -198,11 +198,18 @@ def self.feature_provided?(feature, expanded) # MRI: loaded_feature_path # Search if $LOAD_PATH[i]/feature corresponds to loaded_feature. # Returns the $LOAD_PATH entry containing feature. - def self.loaded_feature_path(loaded_feature, feature, load_path) + def self.feature_path_loaded?(loaded_feature, feature, load_path) name_ext = extension(loaded_feature) - load_path.find do |p| - loaded_feature == "#{p}/#{feature}#{name_ext}" || loaded_feature == "#{p}/#{feature}" + + if name_ext && (suffix_with_ext = "/#{feature}#{name_ext}") && loaded_feature.end_with?(suffix_with_ext) + path = loaded_feature[0...-suffix_with_ext.size] + elsif loaded_feature.end_with?(feature) && loaded_feature.getbyte(-(feature.bytesize + 1)) == 47 # '/'.ord = 47 + path = loaded_feature[0...-(feature.size + 1)] + else + return false end + + load_path.include?(path) end # MRI: rb_provide_feature