Don't lookup finalizers if FL_FINALIZE flag not set

The FL_FINALIZE flag is set when there are finalizers for the object. We
can improver performance by not looking up in the table if the flag is
not set.

Using the following C extension:

    #include "ruby/ruby.h"

    static void data_free(void *_ptr) {}

    static const rb_data_type_t data_type = {
        "my_type",
        {
            NULL,
            data_free,
        },
        0, 0, 0
    };

    static VALUE data_alloc(VALUE klass) {
        return TypedData_Wrap_Struct(klass, &data_type, (void *)1);
    }

    void Init_myext(void) {
        VALUE my_klass = rb_define_class("MyClass", rb_cObject);
        rb_define_alloc_func(my_klass, data_alloc);
    }

And the following benchmark:

    require "benchmark"

    final_objs = 1_000_000.times.map do
      o = Object.new
      ObjectSpace.define_finalizer(o, proc {})
      o
    end

    puts(Benchmark.measure do
      100_000_000.times do
        MyClass.new
      end
    end)

Before:

    10.974190   0.355037  11.329227 ( 11.416772)

After:

    7.664310   0.347598   8.011908 (  8.268969)
This commit is contained in:
Peter Zhu 2024-02-23 13:50:46 -05:00
parent 1a57fee7af
commit 83e676e5f9

24
gc.c
View File

@ -3143,7 +3143,7 @@ static inline void
make_zombie(rb_objspace_t *objspace, VALUE obj, void (*dfree)(void *), void *data)
{
struct RZombie *zombie = RZOMBIE(obj);
zombie->basic.flags = T_ZOMBIE | (zombie->basic.flags & FL_SEEN_OBJ_ID);
zombie->basic.flags = T_ZOMBIE | (zombie->basic.flags & (FL_SEEN_OBJ_ID | FL_FINALIZE));
zombie->dfree = dfree;
zombie->data = data;
VALUE prev, next = heap_pages_deferred_final;
@ -4115,15 +4115,22 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
static void
run_final(rb_objspace_t *objspace, VALUE zombie)
{
st_data_t key, table;
if (RZOMBIE(zombie)->dfree) {
RZOMBIE(zombie)->dfree(RZOMBIE(zombie)->data);
}
key = (st_data_t)zombie;
if (st_delete(finalizer_table, &key, &table)) {
run_finalizer(objspace, zombie, (VALUE)table);
st_data_t key = (st_data_t)zombie;
if (FL_TEST_RAW(zombie, FL_FINALIZE)) {
st_data_t table;
if (st_delete(finalizer_table, &key, &table)) {
run_finalizer(objspace, zombie, (VALUE)table);
}
else {
rb_bug("FL_FINALIZE flag is set, but finalizers are not found");
}
}
else {
GC_ASSERT(!st_lookup(finalizer_table, key, NULL));
}
}
@ -4299,9 +4306,12 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
st_foreach(finalizer_table, force_chain_object, (st_data_t)&list);
while (list) {
struct force_finalize_list *curr = list;
st_data_t obj = (st_data_t)curr->obj;
run_finalizer(objspace, curr->obj, curr->table);
FL_UNSET(curr->obj, FL_FINALIZE);
st_data_t obj = (st_data_t)curr->obj;
st_delete(finalizer_table, &obj, 0);
list = curr->next;
xfree(curr);
}