[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]