Bug #21032
closed`Module#autoload?` is slow when `$LOAD_PATH` contains a relative path
Description
Reproduction script:
require 'benchmark'
$LOAD_PATH << 'relative-path'
autoload :FOO, '/tmp/foo.rb'
puts Benchmark.realtime {
500_000.times do
Object.autoload?(:FOO)
end
}
The above takes 2.5 to 3 seconds on my machine, but just removing $LOAD_PATH << 'relative-path'
make it complete in 50ms.
It's such a stark difference that I think it is a bug, and it cause Zeitwerk, a very popular gem, to be way slower than it should
when the load path contains relative paths.
I have a patch for it, that passes all tests, but I'd appreciate some eyes on it: https://github.com/ruby/ruby/pull/12562
cc @fxn
Updated by Eregon (Benoit Daloze) about 1 month ago
For curiosity, what's adding a relative path in $LOAD_PATH
? That sounds rather dubious/wrong.
Updated by byroot (Jean Boussier) about 1 month ago
@fxn tracked it down to https://github.com/emailage/Emailage_Ruby/blob/64b9762cda7608ac1eeced2a85ad5f4b7919f4f0/lib/emailage.rb#L1, and yes that's an anti-pattern in my opinion too.
Updated by byroot (Jean Boussier) about 1 month ago
I dug more into this today, based on @nobu's review. autoload?
isn't the only thing slowed down in such case.
Perhaps we should try to emit a performance warning when a relative path or non-string object is appended to $LOAD_PATH
. That would at least make it easier to notice this issue.
The problem of course is that $LOAD_PATH
is just a regular array, so there isn't a clean hook where to check for this.
Updated by Eregon (Benoit Daloze) about 1 month ago
byroot (Jean Boussier) wrote in #note-2:
@fxn tracked it down to https://github.com/emailage/Emailage_Ruby/blob/64b9762cda7608ac1eeced2a85ad5f4b7919f4f0/lib/emailage.rb#L1, and yes that's an anti-pattern in my opinion too.
Right, that's just broken code. It would consider files under lib
in $PWD for require which is wrong.
Perhaps we should try to emit a performance warning when a relative path or non-string object is appended to
$LOAD_PATH
. That would at least make it easier to notice this issue.The problem of course is that
$LOAD_PATH
is just a regular array, so there isn't a clean hook where to check for this.
Maybe just warn on require
or so, i.e. when noticing the path is relative, if it's too hard to notice when it's added?
I think there is also a cache for the expanded load path, maybe that could be a reasonable place to check.
It wouldn't immediately point at the culprit, but printing the $LOADED_FEATURES should help find it (maybe there should be a CLI flag/ENV var to print files as they are loaded, TruffleRuby has that).
Updated by byroot (Jean Boussier) 27 days ago
- Status changed from Open to Closed
I merged https://github.com/ruby/ruby/pull/12562 / d4a1a2780c39bc648496ac92fc6e6ce2eb38ab47 because I couldn't find any case where this would change behavior.
Updated by k0kubun (Takashi Kokubun) 9 days ago
- Backport changed from 3.1: WONTFIX, 3.2: REQUIRED, 3.3: REQUIRED, 3.4: REQUIRED to 3.1: WONTFIX, 3.2: REQUIRED, 3.3: REQUIRED, 3.4: DONE
ruby_3_4 ead3bbc2405ad1df2228c44133ee1c6574ef5973 merged revision(s) d4a1a2780c39bc648496ac92fc6e6ce2eb38ab47.