ruby/id_table.h
Jean Boussier e9fd44dd72 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>
2025-06-02 17:49:53 +02:00

50 lines
2.1 KiB
C

#ifndef RUBY_ID_TABLE_H
#define RUBY_ID_TABLE_H 1
#include "ruby/internal/config.h"
#include <stddef.h>
#include "ruby/ruby.h"
struct rb_id_table;
/* compatible with ST_* */
enum rb_id_table_iterator_result {
ID_TABLE_CONTINUE = ST_CONTINUE,
ID_TABLE_STOP = ST_STOP,
ID_TABLE_DELETE = ST_DELETE,
ID_TABLE_REPLACE = ST_REPLACE,
ID_TABLE_ITERATOR_RESULT_END
};
struct rb_id_table *rb_id_table_create(size_t size);
struct rb_id_table *rb_id_table_init(struct rb_id_table *tbl, size_t capa);
void rb_id_table_free(struct rb_id_table *tbl);
void rb_id_table_free_items(struct rb_id_table *tbl);
void rb_id_table_clear(struct rb_id_table *tbl);
size_t rb_id_table_memsize(const struct rb_id_table *tbl);
int rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val);
int rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp);
int rb_id_table_delete(struct rb_id_table *tbl, ID id);
typedef enum rb_id_table_iterator_result rb_id_table_update_value_callback_func_t(VALUE *val, void *data, int existing);
typedef enum rb_id_table_iterator_result rb_id_table_foreach_func_t(ID id, VALUE val, void *data);
typedef enum rb_id_table_iterator_result rb_id_table_foreach_values_func_t(VALUE val, void *data);
void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data);
void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data);
void rb_id_table_foreach_values_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, rb_id_table_update_value_callback_func_t *replace, void *data);
VALUE rb_managed_id_table_new(size_t capa);
VALUE rb_managed_id_table_dup(VALUE table);
int rb_managed_id_table_insert(VALUE table, ID id, VALUE val);
int rb_managed_id_table_lookup(VALUE table, ID id, VALUE *valp);
size_t rb_managed_id_table_size(VALUE table);
void rb_managed_id_table_foreach(VALUE table, rb_id_table_foreach_func_t *func, void *data);
RUBY_SYMBOL_EXPORT_BEGIN
size_t rb_id_table_size(const struct rb_id_table *tbl);
RUBY_SYMBOL_EXPORT_END
#endif /* RUBY_ID_TABLE_H */