ruby-changes:40852
From: normal <ko1@a...>
Date: Tue, 8 Dec 2015 10:47:05 +0900 (JST)
Subject: [ruby-changes:40852] normal:r52931 (trunk): compile optimized case dispatch for nil/true/false
normal 2015-12-08 10:46:45 +0900 (Tue, 08 Dec 2015) New Revision: 52931 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52931 Log: compile optimized case dispatch for nil/true/false nil/true/false are special literals just like floats, integers, literal strings, and symbols. Optimize when statements with them by using a jump table, too. target 0: a (ruby 2.3.0dev (2015-12-08 trunk 52928) [x86_64-linux]) at "/home/ew/rrrr/b/ruby" target 1: b (ruby 2.3.0dev (2015-12-08 master 52928) [x86_64-linux]) at "/home/ew/ruby/b/ruby" benchmark results: minimum results in each 5 measurements. Execution time (sec) name a b loop_whileloop2 0.102 0.103 vm2_case_lit* 1.657 0.549 Speedup ratio: compare with the result of `a' (greater is better) name b loop_whileloop2 0.988 vm2_case_lit* 3.017 * benchmark/bm_vm2_case_lit.rb: new benchmark * compile.c (case_when_optimizable_literal): add nil/true/false * insns.def (opt_case_dispatch): ditto * vm.c (vm_redefinition_check_flag): ditto * vm.c (vm_init_redefined_flag): ditto * vm_core.h: ditto * object.c (InitVM_Object): define === explicitly for nil/true/false * test/ruby/test_case.rb (test_deoptimize_nil): new test * test/ruby/test_optimization.rb (test_opt_case_dispatch): update (test_eqq): new test [ruby-core:71923] [Feature #11769] Original patch by Aaron Patterson <tenderlove@r...> Added files: trunk/benchmark/bm_vm2_case_lit.rb Modified files: trunk/ChangeLog trunk/compile.c trunk/insns.def trunk/object.c trunk/test/ruby/test_case.rb trunk/test/ruby/test_optimization.rb trunk/vm.c trunk/vm_core.h Index: insns.def =================================================================== --- insns.def (revision 52930) +++ insns.def (revision 52931) @@ -1264,6 +1264,9 @@ opt_case_dispatch https://github.com/ruby/ruby/blob/trunk/insns.def#L1264 key = FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival); } } + case T_TRUE: + case T_FALSE: + case T_NIL: case T_SYMBOL: /* fall through */ case T_FIXNUM: case T_BIGNUM: @@ -1273,6 +1276,9 @@ opt_case_dispatch https://github.com/ruby/ruby/blob/trunk/insns.def#L1276 FIXNUM_REDEFINED_OP_FLAG | FLOAT_REDEFINED_OP_FLAG | BIGNUM_REDEFINED_OP_FLAG | + NIL_REDEFINED_OP_FLAG | + TRUE_REDEFINED_OP_FLAG | + FALSE_REDEFINED_OP_FLAG | STRING_REDEFINED_OP_FLAG)) { st_data_t val; if (st_lookup(RHASH_TBL_RAW(hash), key, &val)) { Index: ChangeLog =================================================================== --- ChangeLog (revision 52930) +++ ChangeLog (revision 52931) @@ -1,3 +1,18 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Dec 8 10:40:21 2015 Eric Wong <e@8...> + + * benchmark/bm_vm2_case_lit.rb: new benchmark + * compile.c (case_when_optimizable_literal): add nil/true/false + * insns.def (opt_case_dispatch): ditto + * vm.c (vm_redefinition_check_flag): ditto + * vm.c (vm_init_redefined_flag): ditto + * vm_core.h: ditto + * object.c (InitVM_Object): define === explicitly for nil/true/false + * test/ruby/test_case.rb (test_deoptimize_nil): new test + * test/ruby/test_optimization.rb (test_opt_case_dispatch): update + (test_eqq): new test + [ruby-core:71923] [Feature #11769] + Original patch by Aaron Patterson <tenderlove@r...> + Tue Dec 8 10:19:02 2015 Jake Worth <jakeworth82@g...> * lib/optparse.rb: fix double word typo in the document. Index: vm_core.h =================================================================== --- vm_core.h (revision 52930) +++ vm_core.h (revision 52931) @@ -545,6 +545,9 @@ typedef struct rb_vm_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L545 #define SYMBOL_REDEFINED_OP_FLAG (1 << 6) #define TIME_REDEFINED_OP_FLAG (1 << 7) #define REGEXP_REDEFINED_OP_FLAG (1 << 8) +#define NIL_REDEFINED_OP_FLAG (1 << 9) +#define TRUE_REDEFINED_OP_FLAG (1 << 10) +#define FALSE_REDEFINED_OP_FLAG (1 << 11) #define BASIC_OP_UNREDEFINED_P(op, klass) (LIKELY((GET_VM()->redefined_flag[(op)]&(klass)) == 0)) Index: object.c =================================================================== --- object.c (revision 52930) +++ object.c (revision 52931) @@ -3468,6 +3468,7 @@ InitVM_Object(void) https://github.com/ruby/ruby/blob/trunk/object.c#L3468 rb_define_method(rb_cNilClass, "&", false_and, 1); rb_define_method(rb_cNilClass, "|", false_or, 1); rb_define_method(rb_cNilClass, "^", false_xor, 1); + rb_define_method(rb_cNilClass, "===", rb_equal, 1); rb_define_method(rb_cNilClass, "nil?", rb_true, 0); rb_undef_alloc_func(rb_cNilClass); @@ -3553,6 +3554,7 @@ InitVM_Object(void) https://github.com/ruby/ruby/blob/trunk/object.c#L3554 rb_define_method(rb_cTrueClass, "&", true_and, 1); rb_define_method(rb_cTrueClass, "|", true_or, 1); rb_define_method(rb_cTrueClass, "^", true_xor, 1); + rb_define_method(rb_cTrueClass, "===", rb_equal, 1); rb_undef_alloc_func(rb_cTrueClass); rb_undef_method(CLASS_OF(rb_cTrueClass), "new"); /* @@ -3566,6 +3568,7 @@ InitVM_Object(void) https://github.com/ruby/ruby/blob/trunk/object.c#L3568 rb_define_method(rb_cFalseClass, "&", false_and, 1); rb_define_method(rb_cFalseClass, "|", false_or, 1); rb_define_method(rb_cFalseClass, "^", false_xor, 1); + rb_define_method(rb_cFalseClass, "===", rb_equal, 1); rb_undef_alloc_func(rb_cFalseClass); rb_undef_method(CLASS_OF(rb_cFalseClass), "new"); /* Index: compile.c =================================================================== --- compile.c (revision 52930) +++ compile.c (revision 52931) @@ -2919,6 +2919,12 @@ case_when_optimizable_literal(NODE * nod https://github.com/ruby/ruby/blob/trunk/compile.c#L2919 } break; } + case NODE_NIL: + return Qnil; + case NODE_TRUE: + return Qtrue; + case NODE_FALSE: + return Qfalse; case NODE_STR: return node->nd_lit = rb_fstring(node->nd_lit); } Index: vm.c =================================================================== --- vm.c (revision 52930) +++ vm.c (revision 52931) @@ -1373,6 +1373,9 @@ vm_redefinition_check_flag(VALUE klass) https://github.com/ruby/ruby/blob/trunk/vm.c#L1373 if (klass == rb_cSymbol) return SYMBOL_REDEFINED_OP_FLAG; if (klass == rb_cTime) return TIME_REDEFINED_OP_FLAG; if (klass == rb_cRegexp) return REGEXP_REDEFINED_OP_FLAG; + if (klass == rb_cNilClass) return NIL_REDEFINED_OP_FLAG; + if (klass == rb_cTrueClass) return TRUE_REDEFINED_OP_FLAG; + if (klass == rb_cFalseClass) return FALSE_REDEFINED_OP_FLAG; return 0; } @@ -1437,7 +1440,8 @@ vm_init_redefined_flag(void) https://github.com/ruby/ruby/blob/trunk/vm.c#L1440 OP(DIV, DIV), (C(Fixnum), C(Float)); OP(MOD, MOD), (C(Fixnum), C(Float)); OP(Eq, EQ), (C(Fixnum), C(Float), C(String)); - OP(Eqq, EQQ), (C(Fixnum), C(Bignum), C(Float), C(Symbol), C(String)); + OP(Eqq, EQQ), (C(Fixnum), C(Bignum), C(Float), C(Symbol), C(String), + C(NilClass), C(TrueClass), C(FalseClass)); OP(LT, LT), (C(Fixnum), C(Float)); OP(LE, LE), (C(Fixnum), C(Float)); OP(GT, GT), (C(Fixnum), C(Float)); Index: benchmark/bm_vm2_case_lit.rb =================================================================== --- benchmark/bm_vm2_case_lit.rb (revision 0) +++ benchmark/bm_vm2_case_lit.rb (revision 52931) @@ -0,0 +1,19 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/bm_vm2_case_lit.rb#L1 +i = 0 +@ret = [ "foo", true, false, :sym, 6, nil, 0.1, 0xffffffffffffffff ] +def foo(i) + @ret[i % @ret.size] +end + +while i<6_000_000 # while loop 2 + case foo(i) + when "foo" then :foo + when true then true + when false then false + when :sym then :sym + when 6 then :fix + when nil then nil + when 0.1 then :float + when 0xffffffffffffffff then :big + end + i += 1 +end Index: test/ruby/test_optimization.rb =================================================================== --- test/ruby/test_optimization.rb (revision 52930) +++ test/ruby/test_optimization.rb (revision 52931) @@ -313,8 +313,11 @@ class TestRubyOptimization < Test::Unit: https://github.com/ruby/ruby/blob/trunk/test/ruby/test_optimization.rb#L313 code = <<-EOF case foo when "foo" then :foo + when true then true + when false then false when :sym then :sym when 6 then :fix + when nil then nil when 0.1 then :float when 0xffffffffffffffff then :big else @@ -323,8 +326,11 @@ class TestRubyOptimization < Test::Unit: https://github.com/ruby/ruby/blob/trunk/test/ruby/test_optimization.rb#L326 EOF check = { 'foo' => :foo, + true => true, + false => false, :sym => :sym, 6 => :fix, + nil => nil, 0.1 => :float, 0xffffffffffffffff => :big, } @@ -349,4 +355,11 @@ class TestRubyOptimization < Test::Unit: https://github.com/ruby/ruby/blob/trunk/test/ruby/test_optimization.rb#L355 end; end end + + def test_eqq + [ nil, true, false, 0.1, :sym, 'str', 0xffffffffffffffff ].each do |v| + k = v.class.to_s + assert_redefine_method(k, '===', "assert_equal(#{v.inspect} === 0, 0)") + end + end end Index: test/ruby/test_case.rb =================================================================== --- test/ruby/test_case.rb (revision 52930) +++ test/ruby/test_case.rb (revision 52931) @@ -121,4 +121,25 @@ class TestCase < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_case.rb#L121 end } end + + module NilEqq + refine NilClass do + def === other + false + end + end + end + + class NilEqqClass + using NilEqq + + def eqq(a) + case a; when nil then nil; else :not_nil; end + end + end + + + def test_deoptimize_nil + assert_equal :not_nil, NilEqqClass.new.eqq(nil) + end end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/