Bug #19378
openWindows: Use less syscalls for faster require of big gems
Description
Hello 🙂
Problem¶
require is slow on windows for big gems. (example: require 'gtk3'=> 3 seconds+). This is a problem for people who want to make cross platform GUI apps with ruby.
Possible Reason¶
As touched on in #15797 it seems like require uses realpath, which is emulated on windows. It checks every parent directory. The same syscalls run many times.
Testfile¶
C:\tmp\speedtest\testrequire.rb:
require __dir__ + "/helloworld1.rb"
require __dir__ + "/helloworld2.rb"
ruby --disable-gems C:\tmp\speedtest\testrequire.rb
Syscalls per File/Directory:¶
- CreateFile
- QueryInformationVolume
- QueryIdInformation
- QueryAllInformationFile
- QueryNameInformationFile
- QueryNameInformationFile
- QueryNormalizedNameInformationFile
- CloseFile
Files/Directories checked¶
- C:\tmp
- C:\tmp\speedtest
- C:\tmp\speedtest\helloworld1.rb
- C:\tmp
- C:\tmp\speedtest
- C:\tmp\speedtest\helloworld2.rb
For two required files Ruby had to do 8*6 = 48 syscalls.
The syscalls orginate from rb_w32_reparse_symlink_p / lstat
Rubygems live in subfolders with 9+ parts: "C:\Ruby32-x64\lib\ruby\gems\3.2.0\gems\glib2-4.0.8\lib\glib2\variant.rb"
Each file takes 8 * 9 = 72+ calls. For variant.rb it is 80 calls.
The result for the syscalls don't change in such a short time, so it should be possible to cache it.
With require_relative it's twice as many calls.
Other testcases¶
Same result:
File.realpath __dir__ + "/helloworld1.rb"
File.realpath __dir__ + "/helloworld2.rb"
File.stat __dir__ + "/helloworld1.rb"
File.stat __dir__ + "/helloworld2.rb"
It does not happen in $LOAD_PATH.resolve_feature_path(dir + "/helloworld1.rb")
Request¶
Would it be possible to cache the stat calls when using require?
I tried to implement a cache inside the ruby source code, but failed.
If not, is there now a way to combine ruby files into one?
I previously talked about require here: YJIT: Windows support lacking.
How to reproduce¶
Ruby versions: At least 3.0+, most likely older ones too.
Tested using Ruby Installer 3.1 and 3.2.
Procmon Software by Sysinternals
Files