Skip to content

Commit

Permalink
YJIT: Fastpath for Module#name
Browse files Browse the repository at this point in the history
Module#name shows up as a top C method callee in lobsters so probably
common enough. It's also easy to substitute thanks to rb_mod_name()
already having no GC yield points.

    klass = BasicObject
    50_000_000.times { klass.name }

    Benchmark 1: /.rubies/post/bin/ruby --yjit mod_name.rb
      Time (mean ± σ):      1.433 s ±  0.010 s    [User: 1.410 s, System: 0.010 s]
      Range (min … max):    1.421 s …  1.449 s    10 runs

    Benchmark 2: /.rubies/mstr/bin/ruby --yjit mod_name.rb
      Time (mean ± σ):      1.491 s ±  0.012 s    [User: 1.468 s, System: 0.010 s]
      Range (min … max):    1.470 s …  1.511 s    10 runs

    Summary
      /.rubies/post/bin/ruby --yjit mod_name.rb ran
        1.04 ± 0.01 times faster than /.rubies/mstr/bin/ruby --yjit mod_name.rb
  • Loading branch information
XrXr committed Oct 7, 2024
1 parent 8d35964 commit 8a1e035
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 0 deletions.
3 changes: 3 additions & 0 deletions variable.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ rb_namespace_p(VALUE obj)
* to not be anonymous. <code>*permanent</code> is set to 1
* if +classpath+ has no anonymous components. There is no builtin
* Ruby level APIs that can change a permanent +classpath+.
*
* YJIT needs this function to not allocate.
*/
static VALUE
classname(VALUE klass, bool *permanent)
Expand Down Expand Up @@ -127,6 +129,7 @@ rb_mod_name0(VALUE klass, bool *permanent)
VALUE
rb_mod_name(VALUE mod)
{
// YJIT needs this function to not allocate.
bool permanent;
return classname(mod, &permanent);
}
Expand Down
1 change: 1 addition & 0 deletions yjit/bindgen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ fn main() {
.allowlist_function("rb_attr_get")
.allowlist_function("rb_ivar_defined")
.allowlist_function("rb_ivar_get")
.allowlist_function("rb_mod_name")

// From internal/vm.h
.allowlist_var("rb_vm_insns_count")
Expand Down
28 changes: 28 additions & 0 deletions yjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5135,6 +5135,33 @@ fn jit_rb_mod_eqq(
return true;
}

// Substitution for rb_mod_name(). Returns the name of a module/class.
fn jit_rb_mod_name(
_jit: &mut JITState,
asm: &mut Assembler,
_ci: *const rb_callinfo,
_cme: *const rb_callable_method_entry_t,
_block: Option<BlockHandler>,
argc: i32,
_known_recv_class: Option<VALUE>,
) -> bool {
if argc != 0 {
return false;
}

asm_comment!(asm, "Module#name");

// rb_mod_name() never allocates, so no preparation needed.
let name = asm.ccall(rb_mod_name as _, vec![asm.stack_opnd(0)]);

let _ = asm.stack_pop(1); // pop self
// call-seq: mod.name -> string or nil
let ret = asm.stack_push(Type::Unknown);
asm.mov(ret, name);

true
}

// Codegen for rb_obj_equal()
// object identity comparison
fn jit_rb_obj_equal(
Expand Down Expand Up @@ -10373,6 +10400,7 @@ pub fn yjit_reg_method_codegen_fns() {
yjit_reg_method(rb_mKernel, "eql?", jit_rb_obj_equal);
yjit_reg_method(rb_cModule, "==", jit_rb_obj_equal);
yjit_reg_method(rb_cModule, "===", jit_rb_mod_eqq);
yjit_reg_method(rb_cModule, "name", jit_rb_mod_name);
yjit_reg_method(rb_cSymbol, "==", jit_rb_obj_equal);
yjit_reg_method(rb_cSymbol, "===", jit_rb_obj_equal);
yjit_reg_method(rb_cInteger, "==", jit_rb_int_equal);
Expand Down
1 change: 1 addition & 0 deletions yjit/src/cruby_bindings.inc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ extern "C" {
pub fn rb_str_buf_append(dst: VALUE, src: VALUE) -> VALUE;
pub fn rb_str_dup(str_: VALUE) -> VALUE;
pub fn rb_str_intern(str_: VALUE) -> VALUE;
pub fn rb_mod_name(mod_: VALUE) -> VALUE;
pub fn rb_ivar_get(obj: VALUE, name: ID) -> VALUE;
pub fn rb_ivar_defined(obj: VALUE, name: ID) -> VALUE;
pub fn rb_attr_get(obj: VALUE, name: ID) -> VALUE;
Expand Down

0 comments on commit 8a1e035

Please sign in to comment.