Skip to content

Remove class alloc check#16571

Merged
jhawthorn merged 1 commit into
ruby:masterfrom
jhawthorn:remove_alloc_class_check
Mar 27, 2026
Merged

Remove class alloc check#16571
jhawthorn merged 1 commit into
ruby:masterfrom
jhawthorn:remove_alloc_class_check

Conversation

@jhawthorn
Copy link
Copy Markdown
Member

@jhawthorn jhawthorn commented Mar 26, 2026

I want to remove the following runtime check in class_call_alloc_func, replacing it with a RUBY_ASSERT (so we would still have the check, but only on a debug build).

if (rb_obj_class(obj) != rb_class_real(klass)) {
    rb_raise(rb_eTypeError, "wrong instance allocation");
}

This checks that the value returned from the function registered with rb_define_alloc_func is of the correct class. When this was first introduced in 1fe40b7 (by Matz on 2001-10-03), allocation was done via user-defined Object#allocate, so it made sense to have a runtime check in release builds. Now that it's defined via rb_define_alloc_func in the C extension API, I don't think it's necessary.

The check is surprisingly expensive. Removing it makes Object.new about 10% faster. It allows the C compiler to optimize the call to the function pointer as a tail-call. Removing this also allows ZJIT/YJIT to call the function directly (ZJIT already does this by having a list of known safe allocation functions). The diff in the assembly from skipping this is substantial https://gist.github.com/jhawthorn/20834b654bec4ba0bafee729592d5156 (though much of that is rb_class_real which I also have plans to improve).

$ ips --ruby './miniruby_before' --ruby './miniruby' -e 'Object.new' --yjit
make: 'miniruby' is up to date.
ruby 4.1.0dev (2026-03-26T21:37:39Z master 174ace6af7) +YJIT +PRISM [x86_64-linux]
          Object.new:    86.046M i/s (± 1.1%, GC 24.6%)

ruby 4.1.0dev (2026-03-26T21:44:56Z remove_alloc_class.. e497b65a12) +YJIT +PRISM [x86_64-linux]
          Object.new:    96.527M i/s (± 0.4%, GC 20.3%)


Summary
  ruby ./miniruby ran
    1.12 ± 0.01 times faster than ruby ./miniruby_before

There's no way for users to ever have seen this check, other than by writing a misbehaving C extension, which returns objects with the wrong class.

This checks that the value returned from the function registered with
rb_define_alloc_func is of the correct class. When this was first
introduced in 1fe40b7 (by Matz on 2001-10-03), allocation was done
via user-defined Object#allocate, so it made sense to have a runtime
check in release builds. Now that it's defined via rb_define_alloc_func
in the C extension API, I don't think it's necessary.

The check is surprisingly expensive. Removing it makes Object.new about
10% faster. It allows the C compiler to optimize the call to the
function pointer as a tail-call. Removing this also allows ZJIT/YJIT to
call the function directly (ZJIT already does this by having a list of
known safe allocation functions).

There's no way for users to ever have seen this check, other than by
writing a misbehaving C extension, which returns objects with the wrong
class.

[Feature #21966]
@jhawthorn jhawthorn force-pushed the remove_alloc_class_check branch from e497b65 to eb7c3f4 Compare March 26, 2026 23:57
@jhawthorn jhawthorn merged commit 851b8f8 into ruby:master Mar 27, 2026
92 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants