From b08dacfea39ad8da3f1fd7fdd0e4538cc892ec44 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Thu, 18 Nov 2021 15:10:20 -0800 Subject: Optimize dynamic string interpolation for symbol/true/false/nil/0-9 This provides a significant speedup for symbol, true, false, nil, and 0-9, class/module, and a small speedup in most other cases. Speedups (using included benchmarks): :symbol :: 60% 0-9 :: 50% Class/Module :: 50% nil/true/false :: 20% integer :: 10% [] :: 10% "" :: 3% One reason this approach is faster is it reduces the number of VM instructions for each interpolated value. Initial idea, approach, and benchmarks from Eric Wong. I applied the same approach against the master branch, updating it to handle the significant internal changes since this was first proposed 4 years ago (such as CALL_INFO/CALL_CACHE -> CALL_DATA). I also expanded it to optimize true/false/nil/0-9/class/module, and added handling of missing methods, refined methods, and RUBY_DEBUG. This renames the tostring insn to anytostring, and adds an objtostring insn that implements the optimization. This requires making a few functions non-static, and adding some non-static functions. This disables 4 YJIT tests. Those tests should be reenabled after YJIT optimizes the new objtostring insn. Implements [Feature #13715] Co-authored-by: Eric Wong Co-authored-by: Alan Wu Co-authored-by: Yusuke Endoh Co-authored-by: Koichi Sasada --- vm_insnhelper.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'vm_insnhelper.c') diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 75809f7d86..9643bdd861 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -4729,6 +4729,70 @@ vm_sendish( #endif } +/* object.c */ +VALUE rb_nil_to_s(VALUE); +VALUE rb_true_to_s(VALUE); +VALUE rb_false_to_s(VALUE); +/* numeric.c */ +VALUE rb_int_to_s(int argc, VALUE *argv, VALUE x); +VALUE rb_fix_to_s(VALUE); +/* variable.c */ +VALUE rb_mod_to_s(VALUE); +VALUE rb_mod_name(VALUE); + +static VALUE +vm_objtostring(const rb_iseq_t *iseq, VALUE recv, CALL_DATA cd) +{ + const struct rb_callcache *cc = vm_search_method((VALUE)iseq, cd, recv); + + switch (TYPE(recv)) { + case T_STRING: + return recv; + case T_SYMBOL: + if (check_cfunc(vm_cc_cme(cc), rb_sym_to_s)) { + // rb_sym_to_s() allocates a mutable string, but since we are only + // going to use this string for interpolation, it's fine to use the + // frozen string. + return rb_sym2str(recv); + } + break; + case T_MODULE: + case T_CLASS: + if (check_cfunc(vm_cc_cme(cc), rb_mod_to_s)) { + // rb_mod_to_s() allocates a mutable string, but since we are only + // going to use this string for interpolation, it's fine to use the + // frozen string. + VALUE val = rb_mod_name(recv); + if (val == Qnil) { + val = rb_mod_to_s(recv); + } + return val; + } + break; + case T_NIL: + if (check_cfunc(vm_cc_cme(cc), rb_nil_to_s)) { + return rb_nil_to_s(recv); + } + break; + case T_TRUE: + if (check_cfunc(vm_cc_cme(cc), rb_true_to_s)) { + return rb_true_to_s(recv); + } + break; + case T_FALSE: + if (check_cfunc(vm_cc_cme(cc), rb_false_to_s)) { + return rb_false_to_s(recv); + } + break; + case T_FIXNUM: + if (check_cfunc(vm_cc_cme(cc), rb_int_to_s)) { + return rb_fix_to_s(recv); + } + break; + } + return Qundef; +} + static VALUE vm_opt_str_freeze(VALUE str, int bop, ID id) { -- cgit v1.2.3