Bug #21032
open`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) 3 days ago
For curiosity, what's adding a relative path in $LOAD_PATH
? That sounds rather dubious/wrong.
Updated by byroot (Jean Boussier) 3 days 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) 2 days 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) 2 days 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).