diff options
author | Eric Wong <e@80x24.org> | 2023-02-24 18:05:36 +0000 |
---|---|---|
committer | Eric Wong <normal@ruby-lang.org> | 2023-02-26 20:39:41 +0000 |
commit | 35136e1e9c232ad7a03407b992b2e86b6df43f63 (patch) | |
tree | 2c60e2d5c9a10089badd41d0f696fe41e70f4a1a /internal | |
parent | 6e6992e5db49a238baf290d9b9b521f6b6be5a19 (diff) | |
download | ruby-35136e1e9c232ad7a03407b992b2e86b6df43f63.tar.gz |
reuse open(2) from rb_file_load_ok on POSIX-like system
When loading Ruby source files, we can save the result of
successful opens as open(2)/openat(2) are a fairly expensive
syscalls. This also avoids a time-of-check-to-time-of-use
(TOCTTOU) problem.
This reduces open(2) syscalls during `require'; but should be
most apparent when users have a small $LOAD_PATH. Users with
large $LOAD_PATH will benefit less since there'll be more
open(2) failures due to ENOENT.
With `strace -c -e openat ruby -e exit' under Linux, this
results in a ~14% reduction of openat(2) syscalls
(glibc uses openat(2) to implement open(2)).
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 296 110 openat
0.00 0.000000 0 254 110 openat
Additionally, the introduction of `struct ruby_file_load_state'
may make future optimizations more apparent.
This change cannot benefit binary (.so) loading since the
dlopen(3) API requires a filename and I'm not aware of an
alternative that takes a pre-existing FD. In typical
situations, Ruby source files outnumber the mount of .so
files.
Diffstat (limited to 'internal')
-rw-r--r-- | internal/file.h | 14 | ||||
-rw-r--r-- | internal/parse.h | 3 |
2 files changed, 16 insertions, 1 deletions
diff --git a/internal/file.h b/internal/file.h index 9c192ff4d1..2768418ac5 100644 --- a/internal/file.h +++ b/internal/file.h @@ -11,6 +11,14 @@ #include "ruby/ruby.h" /* for VALUE */ #include "ruby/encoding.h" /* for rb_encodinng */ +struct ruby_file_load_state { + /* TODO: consider stuffing `VALUE fname' here */ + VALUE filev; + unsigned int is_fifo:1; + unsigned int is_nonblock:1; + /* TODO: DOSISH / __CYGWIN__ maintainer may add xflag here */ +}; + /* file.c */ extern const char ruby_null_device[]; VALUE rb_home_dir_of(VALUE user, VALUE result); @@ -18,12 +26,16 @@ VALUE rb_default_home_dir(VALUE result); VALUE rb_realpath_internal(VALUE basedir, VALUE path, int strict); VALUE rb_check_realpath(VALUE basedir, VALUE path, rb_encoding *origenc); void rb_file_const(const char*, VALUE); -int rb_file_load_ok(const char *); +int rb_file_load_ok(const char *, struct ruby_file_load_state *); VALUE rb_file_expand_path_fast(VALUE, VALUE); VALUE rb_file_expand_path_internal(VALUE, VALUE, int, int, VALUE); VALUE rb_get_path_check_to_string(VALUE); VALUE rb_get_path_check_convert(VALUE); int ruby_is_fd_loadable(int fd); +int ruby_disable_nonblock(int fd); +int ruby_find_file_ext(VALUE *filep, const char *const *ext, + struct ruby_file_load_state *); +VALUE ruby_find_file(VALUE path, struct ruby_file_load_state *); RUBY_SYMBOL_EXPORT_BEGIN /* file.c (export) */ diff --git a/internal/parse.h b/internal/parse.h index f242c384ad..7cadd0cd5b 100644 --- a/internal/parse.h +++ b/internal/parse.h @@ -10,10 +10,13 @@ */ #include "ruby/ruby.h" /* for VALUE */ struct rb_iseq_struct; /* in vm_core.h */ +struct ruby_file_load_state; /* internal/file.h */ /* parse.y */ VALUE rb_parser_set_yydebug(VALUE, VALUE); void *rb_parser_load_file(VALUE parser, VALUE name); +void *rb_parser_load_state(VALUE parser, VALUE name, + struct ruby_file_load_state *); void rb_parser_keep_script_lines(VALUE vparser); void rb_parser_error_tolerant(VALUE vparser); void rb_parser_keep_tokens(VALUE vparser); |