Skip to content

win32/file: cut a CreateFile and a malloc out of rb_file_load_ok - #4

Open
Largo wants to merge 2 commits into
masterfrom
win32-file-load-ok
Open

Largo wants to merge 2 commits into
masterfrom
win32-file-load-ok

Conversation

@Largo

@Largo Largo commented Aug 9, 2026

Copy link
Copy Markdown
Owner

rb_file_load_ok is called for every $LOAD_PATH candidate that exists during require — roughly 2000 times in a heavy one. Two changes:

Skip the redundant CreateFile. The function did GetFileAttributesW (cheap) and then CreateFileW + CloseHandle (~50 µs, two extra syscalls) just to verify ACL readability. For regular .rb/.so files on a normal NTFS volume the attribute check is enough; if access really is denied, the subsequent load step fails with a precise error anyway. The CreateFileW fallback is kept for reparse points, so dangling symlinks and AF_UNIX sockets still report "not loadable" exactly as before.

Stack-buffer the path conversion. Each call did a malloc/free pair via mbstr_to_wstr; typical-length paths fit a stack buffer, and longer ones still take the existing fallback.

Measured on this VM: require "gtk3" ~1755 → ~1721 ms (−1.9%), require "tzinfo" ~145 → ~139 ms (−4.1%). The allocation change is within noise on wall-clock — the Windows low-fragmentation heap makes small allocations nearly free — but it removes ~2000 allocations per gtk3 require (mbstr_to_wstr calls drop 7611 → 5545).

Windows-only. Not built or tested on Windows CI — measured in a local MSYS2/UCRT64 VM.

Largo added 2 commits August 10, 2026 00:27
…files

rb_file_load_ok is called for every $LOAD_PATH candidate that exists
during `require`. It did GetFileAttributesW (cheap) followed by
CreateFileW + CloseHandle (~50us, two extra syscalls) to verify ACL
readability. For 99% of candidates (regular .rb / .so files on a
normal NTFS volume) the attribute check is enough — if access really
is denied the subsequent load step fails with a precise error.

The CreateFileW fallback is preserved for reparse points
(FILE_ATTRIBUTE_REPARSE_POINT) so dangling symlinks / AF_UNIX sockets
keep reporting "not loadable" exactly as before.

Result on this VM (LoadLibrary-heavy and pure-Ruby cases):
  require "gtk3":   ~1755 ms -> ~1721 ms (-34 ms / -1.9%)
  require "tzinfo": ~145 ms  -> ~139 ms  (-6 ms / -4.1%)
  require "sqlite3":  unchanged
rb_file_load_ok is called ~2000 times during a heavy `require`. Each
call did a malloc / free pair via mbstr_to_wstr for the UTF-8 -> UTF-16
path conversion. For typical-length paths (under MAX_PATH + slack)
a stack buffer is sufficient and avoids the heap traffic entirely.

For longer paths the existing mbstr_to_wstr fallback kicks in, so
behavior on long paths is preserved.

Measured wall-clock impact on this VM is within noise — modern
Windows low-fragmentation heap makes small allocations essentially
free. Kept anyway because the pattern is correct and removes ~2000
unnecessary heap allocations per gtk3 require (visible in the
profile counter: mbstr_to_wstr drops from 7611 to 5545).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant