Skip to content

Commit 67fcd2d

Browse files
committed
Add early checks to loaded_feature_path to make it faster
1 parent 68f5b26 commit 67fcd2d

File tree

3 files changed

+37
-29
lines changed

3 files changed

+37
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Compatibility:
1616

1717
Performance:
1818

19+
* Improve `Truffle::FeatureLoader.loaded_feature_path` by removing expensive string ops from a loop. Speeds up feature lookup time (#3010, @itarato).
1920

2021
Changes:
2122

spec/truffle/kernel/feature_loader_spec.rb

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,44 +53,44 @@
5353
end
5454
end
5555

56-
describe "Truffle::FeatureLoader.loaded_feature_path" do
56+
describe "Truffle::FeatureLoader.feature_path_loaded?" do
5757
it "returns path for matching feature" do
5858
load_path = ["/path/ruby/lib/ruby/2.6.0"]
5959
loaded_feature = "/path/ruby/lib/ruby/2.6.0/benchmark.rb"
6060
feature = "benchmark"
61-
path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path)
62-
path.should == load_path[0]
61+
path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path)
62+
path.should be_true
6363

6464
feature = "benchmark.rb"
65-
path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path)
66-
path.should == load_path[0]
65+
path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path)
66+
path.should be_true
6767
end
6868

69-
it "returns nil for missing features" do
69+
it "returns false for missing features" do
7070
load_path = ["/path/ruby/lib/ruby/2.6.0"]
71-
path = Truffle::FeatureLoader.loaded_feature_path("/path/ruby/lib/ruby/2.6.0/benchmark.rb", "missing", load_path)
72-
path.should == nil
71+
path = Truffle::FeatureLoader.feature_path_loaded?("/path/ruby/lib/ruby/2.6.0/benchmark.rb", "missing", load_path)
72+
path.should be_false
7373

7474
long_feature = "/path/ruby/lib/ruby/2.6.0/extra-path/benchmark.rb"
75-
path = Truffle::FeatureLoader.loaded_feature_path("/path/ruby/lib/ruby/2.6.0/benchmark.rb", long_feature, load_path)
76-
path.should == nil
75+
path = Truffle::FeatureLoader.feature_path_loaded?("/path/ruby/lib/ruby/2.6.0/benchmark.rb", long_feature, load_path)
76+
path.should be_false
7777
end
7878

7979
it "returns correct paths for non-rb paths" do
8080
load_path = ["/path/ruby/lib/ruby/2.6.0"]
8181
loaded_feature = "/path/ruby/lib/ruby/2.6.0/benchmark.so"
8282

8383
feature = "benchmark"
84-
path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path)
85-
path.should == load_path[0]
84+
path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path)
85+
path.should be_true
8686

8787
feature = "benchmark.so"
88-
path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path)
89-
path.should == load_path[0]
88+
path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path)
89+
path.should be_true
9090

9191
feature = "benchmark.rb"
92-
path = Truffle::FeatureLoader.loaded_feature_path(loaded_feature, feature, load_path)
93-
path.should == nil
92+
path = Truffle::FeatureLoader.feature_path_loaded?(loaded_feature, feature, load_path)
93+
path.should be_false
9494
end
9595

9696
end

src/main/ruby/truffleruby/core/truffle/feature_loader.rb

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,16 @@ def self.feature_provided?(feature, expanded)
165165
loaded_feature = $LOADED_FEATURES[fe.index]
166166

167167
next if loaded_feature.size < feature.size
168-
feature_path = if loaded_feature.start_with?(feature)
169-
feature
170-
else
171-
if expanded
172-
nil
173-
else
174-
loaded_feature_path(loaded_feature, feature, get_expanded_load_path)
175-
end
176-
end
177-
if feature_path
168+
found_feature_path = if loaded_feature.start_with?(feature)
169+
true
170+
else
171+
if expanded
172+
false
173+
else
174+
feature_path_loaded?(loaded_feature, feature, get_expanded_load_path)
175+
end
176+
end
177+
if found_feature_path
178178
loaded_feature_ext = extension_symbol(loaded_feature)
179179
if !loaded_feature_ext
180180
return :unknown unless feature_ext
@@ -198,11 +198,18 @@ def self.feature_provided?(feature, expanded)
198198
# MRI: loaded_feature_path
199199
# Search if $LOAD_PATH[i]/feature corresponds to loaded_feature.
200200
# Returns the $LOAD_PATH entry containing feature.
201-
def self.loaded_feature_path(loaded_feature, feature, load_path)
201+
def self.feature_path_loaded?(loaded_feature, feature, load_path)
202202
name_ext = extension(loaded_feature)
203-
load_path.find do |p|
204-
loaded_feature == "#{p}/#{feature}#{name_ext}" || loaded_feature == "#{p}/#{feature}"
203+
204+
if name_ext && (suffix_with_ext = "/#{feature}#{name_ext}") && loaded_feature.end_with?(suffix_with_ext)
205+
path = loaded_feature[0...-suffix_with_ext.size]
206+
elsif loaded_feature.end_with?(feature) && loaded_feature.getbyte(-(feature.bytesize + 1)) == 47 # '/'.ord = 47
207+
path = loaded_feature[0...-(feature.size + 1)]
208+
else
209+
return false
205210
end
211+
212+
load_path.include?(path)
206213
end
207214

208215
# MRI: rb_provide_feature

0 commit comments

Comments
 (0)