mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 08:25:20 +08:00
Change ISEQ_BODY(iseq)->location.pathobj to be a String instead of Array
Any iseq created inside an eval, class_eval or module_eval had its
`location.pathobj` set to an Array of 2 elements where the 2nd element was
`nil`. This is wasteful in terms of memory and also causes longer major GC
times. This was true for the eval forms even when `__FILE__` was given
directly.
Example script:
```
class Foo; end
Foo.class_eval("def hi = 'hi'")
ObjectSpace.each_object(Array) { |obj|
p obj if obj.frozen? && obj.size == 2 && obj[0].to_s.start_with?("(eval at")
}
```
output before:
```
["(eval at ../ruby/test.rb:47)", nil]
["(eval at ../ruby/test.rb:47)", nil]
```
output after: empty
The changes to the 2 structs do not alter the struct sizes (they make use of
padding that was already there).
This also saves ~1000 Arrays at startup, some from the builtin_prelude: arrays
like `["<internal:numeric>", nil]` per iseq created in those files. Around half
of those survive GC and become long-lived Arrays.
This commit is contained in:
1 parent
1b19b75b9c
commit
817e940d8c
5 files changed
+32
-17
Notes:
git
2026-10-13 19:01:32 +00:00
No files matched your search
@@ -12770,7 +12770,7 @@ typedef uint32_t ibf_offset_t;
|
||||
|
||||
#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
|
||||
#ifdef RUBY_DEVEL
|
||||
#define IBF_DEVEL_VERSION 8
|
||||
#define IBF_DEVEL_VERSION 9
|
||||
#define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
|
||||
#else
|
||||
#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
|
||||
@@ -13955,6 +13955,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
|
||||
ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(param_opt_table_offset));
|
||||
ibf_dump_write_small_value(dump, param_keyword_offset);
|
||||
ibf_dump_write_small_value(dump, location_pathobj_index);
|
||||
ibf_dump_write_small_value(dump, body->no_realpath);
|
||||
ibf_dump_write_small_value(dump, location_label_index);
|
||||
ibf_dump_write_small_value(dump, body->location.first_lineno);
|
||||
ibf_dump_write_small_value(dump, body->location.node_id);
|
||||
@@ -14070,6 +14071,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
|
||||
const ibf_offset_t param_opt_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
|
||||
const ibf_offset_t param_keyword_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
|
||||
const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos);
|
||||
const bool no_realpath = (bool)ibf_load_small_value(load, &reading_pos);
|
||||
const VALUE location_label_index = ibf_load_small_value(load, &reading_pos);
|
||||
const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos);
|
||||
const int location_node_id = (int)ibf_load_small_value(load, &reading_pos);
|
||||
@@ -14111,7 +14113,8 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
|
||||
VALUE realpath = Qnil;
|
||||
|
||||
if (RB_TYPE_P(path, T_STRING)) {
|
||||
realpath = path = rb_fstring(path);
|
||||
path = rb_fstring(path);
|
||||
realpath = no_realpath ? Qnil : path;
|
||||
}
|
||||
else if (RB_TYPE_P(path, T_ARRAY)) {
|
||||
VALUE pathobj = path;
|
||||
@@ -15398,6 +15401,7 @@ rb_iseq_dup_with_independent_caches(const rb_iseq_t *src_root)
|
||||
struct rb_iseq_constant_body *cb = ISEQ_BODY(copy);
|
||||
if (!cb->local_iseq) RB_OBJ_WRITE(copy, &cb->local_iseq, sb->local_iseq);
|
||||
RB_OBJ_WRITE(copy, &cb->location.pathobj, sb->location.pathobj);
|
||||
cb->no_realpath = sb->no_realpath;
|
||||
VALUE sl = ISEQ_SCRIPT_LINES(src_root);
|
||||
VALUE cov = ISEQ_COVERAGE(src_root);
|
||||
if (!NIL_P(sl) || !NIL_P(cov)) {
|
||||
|
||||
@@ -659,20 +659,19 @@ iseq_alloc(void)
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_iseq_pathobj_new(VALUE path, VALUE realpath)
|
||||
rb_iseq_pathobj_new(VALUE path, VALUE realpath, bool *no_realpath)
|
||||
{
|
||||
VALUE pathobj;
|
||||
VM_ASSERT(RB_TYPE_P(path, T_STRING));
|
||||
VM_ASSERT(NIL_P(realpath) || RB_TYPE_P(realpath, T_STRING));
|
||||
|
||||
if (path == realpath ||
|
||||
(!NIL_P(realpath) && rb_str_cmp(path, realpath) == 0)) {
|
||||
*no_realpath = NIL_P(realpath);
|
||||
|
||||
if (*no_realpath || path == realpath || rb_str_cmp(path, realpath) == 0) {
|
||||
pathobj = rb_fstring(path);
|
||||
}
|
||||
else {
|
||||
if (!NIL_P(realpath)) {
|
||||
realpath = rb_fstring(realpath);
|
||||
}
|
||||
realpath = rb_fstring(realpath);
|
||||
VALUE fpath = rb_fstring(path);
|
||||
|
||||
pathobj = rb_ary_new_from_args(2, fpath, realpath);
|
||||
@@ -685,8 +684,10 @@ rb_iseq_pathobj_new(VALUE path, VALUE realpath)
|
||||
void
|
||||
rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
|
||||
{
|
||||
RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->location.pathobj,
|
||||
rb_iseq_pathobj_new(path, realpath));
|
||||
bool no_realpath;
|
||||
VALUE pathobj = rb_iseq_pathobj_new(path, realpath, &no_realpath);
|
||||
RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->location.pathobj, pathobj);
|
||||
ISEQ_BODY(iseq)->no_realpath = no_realpath;
|
||||
}
|
||||
|
||||
// Make a dummy iseq for a dummy frame that exposes a path for profilers to inspect
|
||||
@@ -1594,7 +1595,7 @@ rb_iseq_path(const rb_iseq_t *iseq)
|
||||
VALUE
|
||||
rb_iseq_realpath(const rb_iseq_t *iseq)
|
||||
{
|
||||
return pathobj_realpath(ISEQ_BODY(iseq)->location.pathobj);
|
||||
return pathobj_realpath(ISEQ_BODY(iseq)->location.pathobj, ISEQ_BODY(iseq)->no_realpath);
|
||||
}
|
||||
|
||||
VALUE
|
||||
|
||||
@@ -4565,11 +4565,14 @@ proc_binding(VALUE self)
|
||||
if (iseq) {
|
||||
rb_iseq_check(iseq);
|
||||
RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj);
|
||||
bind->no_realpath = ISEQ_BODY(iseq)->no_realpath;
|
||||
bind->first_lineno = ISEQ_BODY(iseq)->location.first_lineno;
|
||||
}
|
||||
else {
|
||||
bool no_realpath;
|
||||
RB_OBJ_WRITE(bindval, &bind->pathobj,
|
||||
rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil));
|
||||
rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil, &no_realpath));
|
||||
bind->no_realpath = no_realpath;
|
||||
bind->first_lineno = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1793,6 +1793,7 @@ rb_vm_make_binding(const rb_execution_context_t *ec, const rb_control_frame_t *s
|
||||
RB_OBJ_WRITE(bindval, &bind->block.as.captured.self, cfp->self);
|
||||
RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, CFP_ISEQ(cfp));
|
||||
RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(CFP_ISEQ(ruby_level_cfp))->location.pathobj);
|
||||
bind->no_realpath = ISEQ_BODY(CFP_ISEQ(ruby_level_cfp))->no_realpath;
|
||||
bind->first_lineno = rb_vm_get_sourceline(ruby_level_cfp);
|
||||
|
||||
return bindval;
|
||||
@@ -1803,7 +1804,7 @@ rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const I
|
||||
{
|
||||
VALUE envval, pathobj = bind->pathobj;
|
||||
VALUE path = pathobj_path(pathobj);
|
||||
VALUE realpath = pathobj_realpath(pathobj);
|
||||
VALUE realpath = pathobj_realpath(pathobj, bind->no_realpath);
|
||||
const struct rb_block *base_block;
|
||||
const rb_env_t *env;
|
||||
rb_execution_context_t *ec = GET_EC();
|
||||
|
||||
@@ -337,7 +337,7 @@ struct rb_execution_context_struct;
|
||||
#endif
|
||||
|
||||
typedef struct rb_iseq_location_struct {
|
||||
VALUE pathobj; /* String (path) or Array [path, realpath]. Frozen. */
|
||||
VALUE pathobj; /* String (path) or Array [path, realpath]. Frozen. See pathobj_realpath(). */
|
||||
VALUE label; /* String */
|
||||
int first_lineno;
|
||||
int node_id;
|
||||
@@ -360,9 +360,13 @@ pathobj_path(VALUE pathobj)
|
||||
}
|
||||
|
||||
static inline VALUE
|
||||
pathobj_realpath(VALUE pathobj)
|
||||
pathobj_realpath(VALUE pathobj, bool no_realpath)
|
||||
{
|
||||
if (RB_TYPE_P(pathobj, T_STRING)) {
|
||||
if (no_realpath) {
|
||||
VM_ASSERT(RB_TYPE_P(pathobj, T_STRING));
|
||||
return Qnil;
|
||||
}
|
||||
else if (RB_TYPE_P(pathobj, T_STRING)) {
|
||||
return pathobj;
|
||||
}
|
||||
else {
|
||||
@@ -554,6 +558,7 @@ struct rb_iseq_constant_body {
|
||||
unsigned int builtin_attrs; // Union of rb_builtin_attr
|
||||
|
||||
bool prism; // ISEQ was generated from prism compiler
|
||||
bool no_realpath; // location.pathobj names no real file (ex: during eval)
|
||||
|
||||
// Set once an EP escape of this iseq has been reported to the enabled JIT.
|
||||
rb_atomic_t jit_ep_escape_recorded;
|
||||
@@ -1414,6 +1419,7 @@ typedef struct {
|
||||
const struct rb_block block;
|
||||
const VALUE pathobj;
|
||||
int first_lineno;
|
||||
bool no_realpath;
|
||||
} rb_binding_t;
|
||||
|
||||
/* used by compile time and send insn */
|
||||
@@ -2055,7 +2061,7 @@ VALUE rb_iseq_path(const rb_iseq_t *iseq);
|
||||
VALUE rb_iseq_realpath(const rb_iseq_t *iseq);
|
||||
RUBY_SYMBOL_EXPORT_END
|
||||
|
||||
VALUE rb_iseq_pathobj_new(VALUE path, VALUE realpath);
|
||||
VALUE rb_iseq_pathobj_new(VALUE path, VALUE realpath, bool *no_realpath);
|
||||
void rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath);
|
||||
|
||||
int rb_ec_frame_method_id_and_class(const rb_execution_context_t *ec, ID *idp, ID *called_idp, VALUE *klassp);
|
||||
|
||||
Reference in new issue
Block a user