mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
shape.c: Implement a lock-free version of get_next_shape_internal
Whenever we run into an inline cache miss when we try to set an ivar, we may need to take the global lock, just to be able to lookup inside `shape->edges`. To solve that, when we're in multi-ractor mode, we can treat the `shape->edges` as immutable. When we need to add a new edge, we first copy the table, and then replace it with CAS. This increases memory allocations, however we expect that creating new transitions becomes increasingly rare over time. ```ruby class A def initialize(bool) @a = 1 if bool @b = 2 else @c = 3 end end def test @d = 4 end end def bench(iterations) i = iterations while i > 0 A.new(true).test A.new(false).test i -= 1 end end if ARGV.first == "ractor" ractors = 8.times.map do Ractor.new do bench(20_000_000 / 8) end end ractors.each(&:take) else bench(20_000_000) end ``` The above benchmark takes 27 seconds in Ractor mode on Ruby 3.4, and only 1.7s with this branch. Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
This commit is contained in:
parent
cbd49ecbbe
commit
e9fd44dd72
Notes:
git
2025-06-02 15:50:08 +00:00
8 changed files with 307 additions and 107 deletions
104
id_table.c
104
id_table.c
|
@ -80,9 +80,10 @@ round_capa(int capa)
|
|||
return (capa + 1) << 2;
|
||||
}
|
||||
|
||||
static struct rb_id_table *
|
||||
rb_id_table_init(struct rb_id_table *tbl, int capa)
|
||||
struct rb_id_table *
|
||||
rb_id_table_init(struct rb_id_table *tbl, size_t s_capa)
|
||||
{
|
||||
int capa = (int)s_capa;
|
||||
MEMZERO(tbl, struct rb_id_table, 1);
|
||||
if (capa > 0) {
|
||||
capa = round_capa(capa);
|
||||
|
@ -96,7 +97,13 @@ struct rb_id_table *
|
|||
rb_id_table_create(size_t capa)
|
||||
{
|
||||
struct rb_id_table *tbl = ALLOC(struct rb_id_table);
|
||||
return rb_id_table_init(tbl, (int)capa);
|
||||
return rb_id_table_init(tbl, capa);
|
||||
}
|
||||
|
||||
void
|
||||
rb_id_table_free_items(struct rb_id_table *tbl)
|
||||
{
|
||||
xfree(tbl->items);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -324,3 +331,94 @@ rb_id_table_foreach_values_with_replace(struct rb_id_table *tbl, rb_id_table_for
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
managed_id_table_free(void *data)
|
||||
{
|
||||
struct rb_id_table *tbl = (struct rb_id_table *)data;
|
||||
rb_id_table_free_items(tbl);
|
||||
}
|
||||
|
||||
static size_t
|
||||
managed_id_table_memsize(const void *data)
|
||||
{
|
||||
const struct rb_id_table *tbl = (const struct rb_id_table *)data;
|
||||
return rb_id_table_memsize(tbl) - sizeof(struct rb_id_table);
|
||||
}
|
||||
|
||||
static const rb_data_type_t managed_id_table_type = {
|
||||
.wrap_struct_name = "VM/managed_id_table",
|
||||
.function = {
|
||||
.dmark = NULL, // Nothing to mark
|
||||
.dfree = (RUBY_DATA_FUNC)managed_id_table_free,
|
||||
.dsize = managed_id_table_memsize,
|
||||
},
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
|
||||
};
|
||||
|
||||
static inline struct rb_id_table *
|
||||
managed_id_table_ptr(VALUE obj)
|
||||
{
|
||||
return RTYPEDDATA_GET_DATA(obj);
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_managed_id_table_new(size_t capa)
|
||||
{
|
||||
struct rb_id_table *tbl;
|
||||
VALUE obj = TypedData_Make_Struct(0, struct rb_id_table, &managed_id_table_type, tbl);
|
||||
rb_id_table_init(tbl, capa);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static enum rb_id_table_iterator_result
|
||||
managed_id_table_dup_i(ID id, VALUE val, void *data)
|
||||
{
|
||||
struct rb_id_table *new_tbl = (struct rb_id_table *)data;
|
||||
rb_id_table_insert(new_tbl, id, val);
|
||||
return ID_TABLE_CONTINUE;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_managed_id_table_dup(VALUE old_table)
|
||||
{
|
||||
RUBY_ASSERT(rb_typeddata_inherited_p(RTYPEDDATA_TYPE(old_table), &managed_id_table_type));
|
||||
|
||||
struct rb_id_table *new_tbl;
|
||||
VALUE obj = TypedData_Make_Struct(0, struct rb_id_table, &managed_id_table_type, new_tbl);
|
||||
struct rb_id_table *old_tbl = RTYPEDDATA_GET_DATA(old_table);
|
||||
rb_id_table_init(new_tbl, old_tbl->num + 1);
|
||||
rb_id_table_foreach(old_tbl, managed_id_table_dup_i, new_tbl);
|
||||
return obj;
|
||||
}
|
||||
|
||||
int
|
||||
rb_managed_id_table_lookup(VALUE table, ID id, VALUE *valp)
|
||||
{
|
||||
RUBY_ASSERT(rb_typeddata_inherited_p(RTYPEDDATA_TYPE(table), &managed_id_table_type));
|
||||
|
||||
return rb_id_table_lookup(RTYPEDDATA_GET_DATA(table), id, valp);
|
||||
}
|
||||
|
||||
int
|
||||
rb_managed_id_table_insert(VALUE table, ID id, VALUE val)
|
||||
{
|
||||
RUBY_ASSERT(rb_typeddata_inherited_p(RTYPEDDATA_TYPE(table), &managed_id_table_type));
|
||||
|
||||
return rb_id_table_insert(RTYPEDDATA_GET_DATA(table), id, val);
|
||||
}
|
||||
|
||||
size_t
|
||||
rb_managed_id_table_size(VALUE table)
|
||||
{
|
||||
RUBY_ASSERT(rb_typeddata_inherited_p(RTYPEDDATA_TYPE(table), &managed_id_table_type));
|
||||
|
||||
return rb_id_table_size(RTYPEDDATA_GET_DATA(table));
|
||||
}
|
||||
|
||||
void
|
||||
rb_managed_id_table_foreach(VALUE table, rb_id_table_foreach_func_t *func, void *data)
|
||||
{
|
||||
RUBY_ASSERT(rb_typeddata_inherited_p(RTYPEDDATA_TYPE(table), &managed_id_table_type));
|
||||
|
||||
rb_id_table_foreach(RTYPEDDATA_GET_DATA(table), func, data);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue