mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Implement gen_fields_tbl
cache
There is a high likelyhood that `rb_obj_fields` is called consecutively for the same object. If we keep a cache of the last IMEMO/fields we interacted with, we can save having to lookup the `gen_fields_tbl`, synchronize the VM lock, etc. On yjit-bench's, I instrumented the hit rate of this cache at: - `shipit`: 38%, with 111k hits. - `lobsters`: 59%, with 367k hits. - `rubocop`: 100% with only 300 hits. I also ran a micro-benchmark which shows that ivar access is: - 1.25x faster when the cache is hit in single ractor mode. - 2x faster when the cache is hit in multi ractor mode. - 1.06x slower when the cache miss in single ractor mode. - 1.01x slower when the cache miss in multi ractor mode. ```yml prelude: | class GenIvar < Array def initialize(...) super @iv = 1 end attr_reader :iv end a = GenIvar.new b = GenIvar.new benchmark: hit: a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; miss: a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; ``` Single ractor: ``` compare-ruby: ruby 3.5.0dev (2025-08-12T02:14:57Z master428937a536
) +YJIT +PRISM [arm64-darwin24] built-ruby: ruby 3.5.0dev (2025-08-12T09:25:35Z gen-fields-cache 9456c35893) +YJIT +PRISM [arm64-darwin24] warming up.. | |compare-ruby|built-ruby| |:-----|-----------:|---------:| |hit | 4.090M| 5.121M| | | -| 1.25x| |miss | 3.756M| 3.534M| | | 1.06x| -| ``` Multi-ractor: ``` compare-ruby: ruby 3.5.0dev (2025-08-12T02:14:57Z master428937a536
) +YJIT +PRISM [arm64-darwin24] built-ruby: ruby 3.5.0dev (2025-08-12T09:25:35Z gen-fields-cache 9456c35893) +YJIT +PRISM [arm64-darwin24] warming up.. | |compare-ruby|built-ruby| |:-----|-----------:|---------:| |hit | 2.205M| 4.460M| | | -| 2.02x| |miss | 2.117M| 2.094M| | | 1.01x| -| ```
This commit is contained in:
parent
10aa4134d4
commit
2083fa89fc
3 changed files with 44 additions and 8 deletions
41
variable.c
41
variable.c
|
@ -1245,9 +1245,19 @@ rb_obj_fields(VALUE obj, ID field_name)
|
||||||
goto generic_fields;
|
goto generic_fields;
|
||||||
default:
|
default:
|
||||||
generic_fields:
|
generic_fields:
|
||||||
RB_VM_LOCKING() {
|
{
|
||||||
if (!st_lookup(generic_fields_tbl_, (st_data_t)obj, (st_data_t *)&fields_obj)) {
|
rb_execution_context_t *ec = GET_EC();
|
||||||
rb_bug("Object is missing entry in generic_fields_tbl");
|
if (ec->gen_fields_cache.obj == obj) {
|
||||||
|
fields_obj = ec->gen_fields_cache.fields_obj;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RB_VM_LOCKING() {
|
||||||
|
if (!st_lookup(generic_fields_tbl_, (st_data_t)obj, (st_data_t *)&fields_obj)) {
|
||||||
|
rb_bug("Object is missing entry in generic_fields_tbl");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ec->gen_fields_cache.fields_obj = fields_obj;
|
||||||
|
ec->gen_fields_cache.obj = obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1275,8 +1285,15 @@ rb_free_generic_ivar(VALUE obj)
|
||||||
goto generic_fields;
|
goto generic_fields;
|
||||||
default:
|
default:
|
||||||
generic_fields:
|
generic_fields:
|
||||||
RB_VM_LOCKING() {
|
{
|
||||||
st_delete(generic_fields_tbl_no_ractor_check(), &key, &value);
|
rb_execution_context_t *ec = GET_EC();
|
||||||
|
if (ec->gen_fields_cache.obj == obj) {
|
||||||
|
ec->gen_fields_cache.obj = Qundef;
|
||||||
|
ec->gen_fields_cache.fields_obj = Qundef;
|
||||||
|
}
|
||||||
|
RB_VM_LOCKING() {
|
||||||
|
st_delete(generic_fields_tbl_no_ractor_check(), &key, &value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RBASIC_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
|
RBASIC_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
|
||||||
|
@ -1307,10 +1324,18 @@ rb_obj_set_fields(VALUE obj, VALUE fields_obj, ID field_name, VALUE original_fie
|
||||||
goto generic_fields;
|
goto generic_fields;
|
||||||
default:
|
default:
|
||||||
generic_fields:
|
generic_fields:
|
||||||
RB_VM_LOCKING() {
|
{
|
||||||
st_insert(generic_fields_tbl_, (st_data_t)obj, (st_data_t)fields_obj);
|
RB_VM_LOCKING() {
|
||||||
|
st_insert(generic_fields_tbl_, (st_data_t)obj, (st_data_t)fields_obj);
|
||||||
|
}
|
||||||
|
RB_OBJ_WRITTEN(obj, original_fields_obj, fields_obj);
|
||||||
|
|
||||||
|
rb_execution_context_t *ec = GET_EC();
|
||||||
|
if (ec->gen_fields_cache.fields_obj != fields_obj) {
|
||||||
|
ec->gen_fields_cache.obj = obj;
|
||||||
|
ec->gen_fields_cache.fields_obj = fields_obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RB_OBJ_WRITTEN(obj, original_fields_obj, fields_obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (original_fields_obj) {
|
if (original_fields_obj) {
|
||||||
|
|
6
vm.c
6
vm.c
|
@ -3441,6 +3441,9 @@ rb_execution_context_update(rb_execution_context_t *ec)
|
||||||
}
|
}
|
||||||
|
|
||||||
ec->storage = rb_gc_location(ec->storage);
|
ec->storage = rb_gc_location(ec->storage);
|
||||||
|
|
||||||
|
ec->gen_fields_cache.obj = rb_gc_location(ec->gen_fields_cache.obj);
|
||||||
|
ec->gen_fields_cache.fields_obj = rb_gc_location(ec->gen_fields_cache.fields_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum rb_id_table_iterator_result
|
static enum rb_id_table_iterator_result
|
||||||
|
@ -3505,6 +3508,9 @@ rb_execution_context_mark(const rb_execution_context_t *ec)
|
||||||
rb_gc_mark(ec->private_const_reference);
|
rb_gc_mark(ec->private_const_reference);
|
||||||
|
|
||||||
rb_gc_mark_movable(ec->storage);
|
rb_gc_mark_movable(ec->storage);
|
||||||
|
|
||||||
|
rb_gc_mark_weak((VALUE *)&ec->gen_fields_cache.obj);
|
||||||
|
rb_gc_mark_weak((VALUE *)&ec->gen_fields_cache.fields_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rb_fiber_mark_self(rb_fiber_t *fib);
|
void rb_fiber_mark_self(rb_fiber_t *fib);
|
||||||
|
|
|
@ -1070,6 +1070,11 @@ struct rb_execution_context_struct {
|
||||||
|
|
||||||
VALUE private_const_reference;
|
VALUE private_const_reference;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
VALUE obj;
|
||||||
|
VALUE fields_obj;
|
||||||
|
} gen_fields_cache;
|
||||||
|
|
||||||
/* for GC */
|
/* for GC */
|
||||||
struct {
|
struct {
|
||||||
VALUE *stack_start;
|
VALUE *stack_start;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue