mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Lazily create objspace->id_to_obj_tbl
This inverse table is only useful if `ObjectSpace._id2ref` is used,
which is extremely rare. The only notable exception is the `drb` gem
and even then it has an option not to rely on `_id2ref`.
So if we assume this table will never be looked up, we can just
not maintain it, and if it turns out `_id2ref` is called, we
can lock the VM and re-build it.
```
compare-ruby: ruby 3.5.0dev (2025-04-10T09:44:40Z master 684cfa42d7
) +YJIT +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-04-10T10:13:43Z lazy-id-to-obj d3aa9626cc) +YJIT +PRISM [arm64-darwin24]
warming up..
| |compare-ruby|built-ruby|
|:----------|-----------:|---------:|
|baseline | 26.364M| 25.974M|
| | 1.01x| -|
|object_id | 10.293M| 14.202M|
| | -| 1.38x|
```
This commit is contained in:
parent
b68172caad
commit
0606046c1a
Notes:
git
2025-04-15 00:18:12 +00:00
5 changed files with 74 additions and 20 deletions
4
benchmark/object_id.yml
Normal file
4
benchmark/object_id.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
benchmark:
|
||||||
|
baseline: "Object.new"
|
||||||
|
object_id: "Object.new.object_id"
|
||||||
|
# loop_count: 100000
|
6
gc.c
6
gc.c
|
@ -1794,11 +1794,11 @@ id2ref(VALUE objid)
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_raise(rb_eRangeError, "%p is not symbol id value", (void *)ptr);
|
rb_raise(rb_eRangeError, "%p is not a symbol id value", (void *)ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_int2str(objid, 10));
|
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not an id value", rb_int2str(objid, 10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1807,7 +1807,7 @@ id2ref(VALUE objid)
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is id of the unshareable object on multi-ractor", rb_int2str(objid, 10));
|
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is the id of an unshareable object on multi-ractor", rb_int2str(objid, 10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1585,7 +1585,9 @@ rb_gc_impl_object_id(void *objspace_ptr, VALUE obj)
|
||||||
objspace->next_object_id += OBJ_ID_INCREMENT;
|
objspace->next_object_id += OBJ_ID_INCREMENT;
|
||||||
|
|
||||||
st_insert(objspace->obj_to_id_tbl, (st_data_t)obj, (st_data_t)id);
|
st_insert(objspace->obj_to_id_tbl, (st_data_t)obj, (st_data_t)id);
|
||||||
st_insert(objspace->id_to_obj_tbl, (st_data_t)id, (st_data_t)obj);
|
if (RB_UNLIKELY(objspace->id_to_obj_tbl)) {
|
||||||
|
st_insert(objspace->id_to_obj_tbl, (st_data_t)id, (st_data_t)obj);
|
||||||
|
}
|
||||||
FL_SET(obj, FL_SEEN_OBJ_ID);
|
FL_SET(obj, FL_SEEN_OBJ_ID);
|
||||||
}
|
}
|
||||||
rb_gc_vm_unlock(lev);
|
rb_gc_vm_unlock(lev);
|
||||||
|
@ -1593,22 +1595,40 @@ rb_gc_impl_object_id(void *objspace_ptr, VALUE obj)
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
build_id_to_obj_i(st_data_t key, st_data_t value, st_data_t data)
|
||||||
|
{
|
||||||
|
st_table *id_to_obj_tbl = (st_table *)data;
|
||||||
|
st_insert(id_to_obj_tbl, value, key);
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_gc_impl_object_id_to_ref(void *objspace_ptr, VALUE object_id)
|
rb_gc_impl_object_id_to_ref(void *objspace_ptr, VALUE object_id)
|
||||||
{
|
{
|
||||||
rb_objspace_t *objspace = objspace_ptr;
|
rb_objspace_t *objspace = objspace_ptr;
|
||||||
|
|
||||||
|
unsigned int lev = rb_gc_vm_lock();
|
||||||
|
|
||||||
|
if (!objspace->id_to_obj_tbl) {
|
||||||
|
objspace->id_to_obj_tbl = st_init_table_with_size(&object_id_hash_type, st_table_size(objspace->obj_to_id_tbl));
|
||||||
|
st_foreach(objspace->obj_to_id_tbl, build_id_to_obj_i, (st_data_t)objspace->id_to_obj_tbl);
|
||||||
|
}
|
||||||
|
|
||||||
VALUE obj;
|
VALUE obj;
|
||||||
if (st_lookup(objspace->id_to_obj_tbl, object_id, &obj) &&
|
bool found = st_lookup(objspace->id_to_obj_tbl, object_id, &obj) && !rb_gc_impl_garbage_object_p(objspace, obj);
|
||||||
!rb_gc_impl_garbage_object_p(objspace, obj)) {
|
|
||||||
|
rb_gc_vm_unlock(lev);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rb_funcall(object_id, rb_intern(">="), 1, ULL2NUM(objspace->next_object_id))) {
|
if (rb_funcall(object_id, rb_intern(">="), 1, ULL2NUM(objspace->next_object_id))) {
|
||||||
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10)));
|
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not an id value", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10)));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is recycled object", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10)));
|
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is a recycled object", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2644,7 +2664,9 @@ obj_free_object_id(rb_objspace_t *objspace, VALUE obj)
|
||||||
|
|
||||||
if (st_delete(objspace->obj_to_id_tbl, &o, &id)) {
|
if (st_delete(objspace->obj_to_id_tbl, &o, &id)) {
|
||||||
GC_ASSERT(id);
|
GC_ASSERT(id);
|
||||||
st_delete(objspace->id_to_obj_tbl, &id, NULL);
|
if (RB_UNLIKELY(objspace->id_to_obj_tbl)) {
|
||||||
|
st_delete(objspace->id_to_obj_tbl, &id, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_bug("Object ID seen, but not in mapping table: %s", rb_obj_info(obj));
|
rb_bug("Object ID seen, but not in mapping table: %s", rb_obj_info(obj));
|
||||||
|
@ -7168,7 +7190,9 @@ gc_update_references(rb_objspace_t *objspace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gc_ref_update_table_values_only(objspace->obj_to_id_tbl);
|
gc_ref_update_table_values_only(objspace->obj_to_id_tbl);
|
||||||
gc_update_table_refs(objspace->id_to_obj_tbl);
|
if (RB_UNLIKELY(objspace->id_to_obj_tbl)) {
|
||||||
|
gc_update_table_refs(objspace->id_to_obj_tbl);
|
||||||
|
}
|
||||||
gc_update_table_refs(finalizer_table);
|
gc_update_table_refs(finalizer_table);
|
||||||
|
|
||||||
rb_gc_update_vm_references((void *)objspace);
|
rb_gc_update_vm_references((void *)objspace);
|
||||||
|
@ -9282,7 +9306,10 @@ rb_gc_impl_objspace_free(void *objspace_ptr)
|
||||||
heap->total_slots = 0;
|
heap->total_slots = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
st_free_table(objspace->id_to_obj_tbl);
|
|
||||||
|
if (objspace->id_to_obj_tbl) {
|
||||||
|
st_free_table(objspace->id_to_obj_tbl);
|
||||||
|
}
|
||||||
st_free_table(objspace->obj_to_id_tbl);
|
st_free_table(objspace->obj_to_id_tbl);
|
||||||
|
|
||||||
free_stack_chunks(&objspace->mark_stack);
|
free_stack_chunks(&objspace->mark_stack);
|
||||||
|
@ -9423,7 +9450,7 @@ rb_gc_impl_objspace_init(void *objspace_ptr)
|
||||||
heap_page_alloc_use_mmap = INIT_HEAP_PAGE_ALLOC_USE_MMAP;
|
heap_page_alloc_use_mmap = INIT_HEAP_PAGE_ALLOC_USE_MMAP;
|
||||||
#endif
|
#endif
|
||||||
objspace->next_object_id = OBJ_ID_INITIAL;
|
objspace->next_object_id = OBJ_ID_INITIAL;
|
||||||
objspace->id_to_obj_tbl = st_init_table(&object_id_hash_type);
|
objspace->id_to_obj_tbl = NULL;
|
||||||
objspace->obj_to_id_tbl = st_init_numtable();
|
objspace->obj_to_id_tbl = st_init_numtable();
|
||||||
#if RGENGC_ESTIMATE_OLDMALLOC
|
#if RGENGC_ESTIMATE_OLDMALLOC
|
||||||
objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
|
objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
|
||||||
|
|
|
@ -366,7 +366,9 @@ rb_mmtk_update_obj_id_tables(void)
|
||||||
struct objspace *objspace = rb_gc_get_objspace();
|
struct objspace *objspace = rb_gc_get_objspace();
|
||||||
|
|
||||||
st_foreach(objspace->obj_to_id_tbl, rb_mmtk_update_obj_id_tables_obj_to_id_i, 0);
|
st_foreach(objspace->obj_to_id_tbl, rb_mmtk_update_obj_id_tables_obj_to_id_i, 0);
|
||||||
st_foreach(objspace->id_to_obj_tbl, rb_mmtk_update_obj_id_tables_id_to_obj_i, 0);
|
if (objspace->id_to_obj_tbl) {
|
||||||
|
st_foreach(objspace->id_to_obj_tbl, rb_mmtk_update_obj_id_tables_id_to_obj_i, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1091,7 +1093,7 @@ static const struct st_hash_type object_id_hash_type = {
|
||||||
static void
|
static void
|
||||||
objspace_obj_id_init(struct objspace *objspace)
|
objspace_obj_id_init(struct objspace *objspace)
|
||||||
{
|
{
|
||||||
objspace->id_to_obj_tbl = st_init_table(&object_id_hash_type);
|
objspace->id_to_obj_tbl = NULL;
|
||||||
objspace->obj_to_id_tbl = st_init_numtable();
|
objspace->obj_to_id_tbl = st_init_numtable();
|
||||||
objspace->next_object_id = OBJ_ID_INITIAL;
|
objspace->next_object_id = OBJ_ID_INITIAL;
|
||||||
}
|
}
|
||||||
|
@ -1119,7 +1121,9 @@ rb_gc_impl_object_id(void *objspace_ptr, VALUE obj)
|
||||||
objspace->next_object_id += OBJ_ID_INCREMENT;
|
objspace->next_object_id += OBJ_ID_INCREMENT;
|
||||||
|
|
||||||
st_insert(objspace->obj_to_id_tbl, (st_data_t)obj, (st_data_t)id);
|
st_insert(objspace->obj_to_id_tbl, (st_data_t)obj, (st_data_t)id);
|
||||||
st_insert(objspace->id_to_obj_tbl, (st_data_t)id, (st_data_t)obj);
|
if (RB_UNLIKELY(objspace->id_to_obj_tbl)) {
|
||||||
|
st_insert(objspace->id_to_obj_tbl, (st_data_t)id, (st_data_t)obj);
|
||||||
|
}
|
||||||
FL_SET(obj, FL_SEEN_OBJ_ID);
|
FL_SET(obj, FL_SEEN_OBJ_ID);
|
||||||
}
|
}
|
||||||
rb_gc_vm_unlock(lev);
|
rb_gc_vm_unlock(lev);
|
||||||
|
@ -1127,22 +1131,41 @@ rb_gc_impl_object_id(void *objspace_ptr, VALUE obj)
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
build_id_to_obj_i(st_data_t key, st_data_t value, st_data_t data)
|
||||||
|
{
|
||||||
|
st_table *id_to_obj_tbl = (st_table *)data;
|
||||||
|
st_insert(id_to_obj_tbl, value, key);
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_gc_impl_object_id_to_ref(void *objspace_ptr, VALUE object_id)
|
rb_gc_impl_object_id_to_ref(void *objspace_ptr, VALUE object_id)
|
||||||
{
|
{
|
||||||
struct objspace *objspace = objspace_ptr;
|
struct objspace *objspace = objspace_ptr;
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int lev = rb_gc_vm_lock();
|
||||||
|
|
||||||
|
if (!objspace->id_to_obj_tbl) {
|
||||||
|
objspace->id_to_obj_tbl = st_init_table_with_size(&object_id_hash_type, st_table_size(objspace->obj_to_id_tbl));
|
||||||
|
st_foreach(objspace->obj_to_id_tbl, build_id_to_obj_i, (st_data_t)objspace->id_to_obj_tbl);
|
||||||
|
}
|
||||||
|
|
||||||
VALUE obj;
|
VALUE obj;
|
||||||
if (st_lookup(objspace->id_to_obj_tbl, object_id, &obj) &&
|
bool found = st_lookup(objspace->id_to_obj_tbl, object_id, &obj) && !rb_gc_impl_garbage_object_p(objspace, obj);
|
||||||
!rb_gc_impl_garbage_object_p(objspace, obj)) {
|
|
||||||
|
rb_gc_vm_unlock(lev);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rb_funcall(object_id, rb_intern(">="), 1, ULL2NUM(objspace->next_object_id))) {
|
if (rb_funcall(object_id, rb_intern(">="), 1, ULL2NUM(objspace->next_object_id))) {
|
||||||
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not id value", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10)));
|
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is not an id value", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10)));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is recycled object", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10)));
|
rb_raise(rb_eRangeError, "%+"PRIsVALUE" is a recycled object", rb_funcall(object_id, rb_intern("to_s"), 1, INT2FIX(10)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ End
|
||||||
# RB_STATIC_SYM_P checks for static symbols by checking that the bottom
|
# RB_STATIC_SYM_P checks for static symbols by checking that the bottom
|
||||||
# 8 bits of the object is equal to RUBY_SYMBOL_FLAG, so we need to make
|
# 8 bits of the object is equal to RUBY_SYMBOL_FLAG, so we need to make
|
||||||
# sure that the bottom 8 bits remain unchanged.
|
# sure that the bottom 8 bits remain unchanged.
|
||||||
msg = /is not symbol id value/
|
msg = /is not a symbol id value/
|
||||||
assert_raise_with_message(RangeError, msg) { ObjectSpace._id2ref(:a.object_id + 256) }
|
assert_raise_with_message(RangeError, msg) { ObjectSpace._id2ref(:a.object_id + 256) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue