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

ruby-changes:32472

From: tmm1 <ko1@a...>
Date: Fri, 10 Jan 2014 13:54:18 +0900 (JST)
Subject: [ruby-changes:32472] tmm1:r44551 (trunk): insns.def: add opt path for Hash#[] and Hash#[]= used with str literal keys

tmm1	2014-01-10 13:54:08 +0900 (Fri, 10 Jan 2014)

  New Revision: 44551

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=44551

  Log:
    insns.def: add opt path for Hash#[] and Hash#[]= used with str literal keys
    
    * insns.def (opt_aref_with): new instruction to optimize Hash#[],
      removing any allocation overhead when used with a string literal
      key. Patch by normalperson (Eric Wong). [ruby-core:59640] [Bug #9382]
    * insns.def (opt_aset_with): new instruction to optimize Hash#[]=
    * compile.c (iseq_compile_each): compiler shortcuts for new
      instructions
    * hash.c (static VALUE rb_hash_compare_by_id_p): fix documentation for
      Hash#compare_by_identity to reflect frozen string sharing
    * test/ruby/test_hash.rb (class TestHash): test for new behavior

  Modified files:
    trunk/ChangeLog
    trunk/compile.c
    trunk/hash.c
    trunk/insns.def
    trunk/test/ruby/test_hash.rb
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 44550)
+++ ChangeLog	(revision 44551)
@@ -1,3 +1,15 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Fri Jan 10 13:34:04 2014  Aman Gupta <ruby@t...>
+
+	* insns.def (opt_aref_with): new instruction to optimize Hash#[],
+	  removing any allocation overhead when used with a string literal
+	  key. Patch by normalperson (Eric Wong). [ruby-core:59640] [Bug #9382]
+	* insns.def (opt_aset_with): new instruction to optimize Hash#[]=
+	* compile.c (iseq_compile_each): compiler shortcuts for new
+	  instructions
+	* hash.c (static VALUE rb_hash_compare_by_id_p): fix documentation for
+	  Hash#compare_by_identity to reflect frozen string sharing
+	* test/ruby/test_hash.rb (class TestHash): test for new behavior
+
 Fri Jan 10 06:23:21 2014  Benoit Daloze  <eregontp@g...>
 
 	* range.c (Range#size): [DOC] improve description and add examples.
Index: insns.def
===================================================================
--- insns.def	(revision 44550)
+++ insns.def	(revision 44551)
@@ -1903,6 +1903,47 @@ opt_aset https://github.com/ruby/ruby/blob/trunk/insns.def#L1903
 
 /**
   @c optimize
+  @e recv[str] = set
+  @j i recv[str] = set+ */
+DEFINE_INSN
+opt_aset_with
+(CALL_INFO ci, VALUE key)
+(VALUE recv, VALUE val)
+(VALUE val)
+{
+    if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
+	rb_hash_aset(recv, key, val);
+    } else {
+	PUSH(recv);
+	PUSH(rb_str_resurrect(key));
+	PUSH(val);
+	CALL_SIMPLE_METHOD(recv);
+    }
+}
+
+/**
+  @c optimize
+  @e recv[str]
+  @j i recv[str]+ */
+DEFINE_INSN
+opt_aref_with
+(CALL_INFO ci, VALUE key)
+(VALUE recv)
+(VALUE val)
+{
+    if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
+	val = rb_hash_aref(recv, key);
+    } else {
+	PUSH(recv);
+	PUSH(rb_str_resurrect(key));
+	CALL_SIMPLE_METHOD(recv);
+    }
+}
+
+/**
+  @c optimize
   @e optimized length
   @j i recv.length()  */
Index: compile.c
===================================================================
--- compile.c	(revision 44550)
+++ compile.c	(revision 44551)
@@ -4319,6 +4319,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L4319
 	break;
       }
       case NODE_CALL:
+	/* optimization shortcut
+	 *   "literal".freeze -> opt_str_freeze("literal")
+	 */
 	if (node->nd_recv && nd_type(node->nd_recv) == NODE_STR &&
 	    node->nd_mid == idFreeze && node->nd_args == NULL)
 	{
@@ -4330,6 +4333,23 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L4333
 	    }
 	    break;
 	}
+	/* optimization shortcut
+	 *   obj["literal"] -> opt_aref_with(obj, "literal")
+	 */
+	if (node->nd_mid == idAREF && node->nd_recv != (NODE *)1 && node->nd_args &&
+	    nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 1 &&
+	    nd_type(node->nd_args->nd_head) == NODE_STR)
+	{
+	    VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
+	    node->nd_args->nd_head->nd_lit = str;
+	    COMPILE(ret, "recv", node->nd_recv);
+	    ADD_INSN2(ret, line, opt_aref_with,
+		      new_callinfo(iseq, idAREF, 1, 0, 0), str);
+	    if (poped) {
+		ADD_INSN(ret, line, pop);
+	    }
+	    break;
+	}
       case NODE_FCALL:
       case NODE_VCALL:{		/* VCALL: variable or call */
 	/*
@@ -5300,6 +5320,25 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L5320
 	VALUE flag = 0;
 	VALUE argc;
 
+	/* optimization shortcut
+	 *   obj["literal"] = value -> opt_aset_with(obj, "literal", value)
+	 */
+	if (node->nd_mid == idASET && node->nd_recv != (NODE *)1 && node->nd_args &&
+	    nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 2 &&
+	    nd_type(node->nd_args->nd_head) == NODE_STR)
+	{
+	    VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
+	    node->nd_args->nd_head->nd_lit = str;
+	    COMPILE(ret, "recv", node->nd_recv);
+	    COMPILE(ret, "value", node->nd_args->nd_next->nd_head);
+	    ADD_INSN2(ret, line, opt_aset_with,
+		      new_callinfo(iseq, idASET, 2, 0, 0), str);
+	    if (poped) {
+		ADD_INSN(ret, line, pop);
+	    }
+	    break;
+	}
+
 	INIT_ANCHOR(recv);
 	INIT_ANCHOR(args);
 	argc = setup_args(iseq, args, node->nd_args, &flag);
Index: hash.c
===================================================================
--- hash.c	(revision 44550)
+++ hash.c	(revision 44551)
@@ -2399,7 +2399,7 @@ static VALUE rb_hash_compare_by_id_p(VAL https://github.com/ruby/ruby/blob/trunk/hash.c#L2399
  *     h1["a"]        #=> 100
  *     h1.compare_by_identity
  *     h1.compare_by_identity? #=> true
- *     h1["a"]        #=> nil  # different objects.
+ *     h1["a".dup]    #=> nil  # different objects.
  *     h1[:c]         #=> "c"  # same symbols are all same.
  *
  */
Index: test/ruby/test_hash.rb
===================================================================
--- test/ruby/test_hash.rb	(revision 44550)
+++ test/ruby/test_hash.rb	(revision 44551)
@@ -209,6 +209,20 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L209
     assert_equal(256,     h[z])
   end
 
+  def test_AREF_fstring_key
+    h = {"abc" => 1}
+    before = GC.stat(:total_allocated_object)
+    5.times{ h["abc"] }
+    assert_equal before, GC.stat(:total_allocated_object)
+  end
+
+  def test_ASET_fstring_key
+    a, b = {}, {}
+    assert_equal 1, a["abc"] = 1
+    assert_equal 1, b["abc"] = 1
+    assert_same a.keys[0], b.keys[0]
+  end
+
   def test_NEWHASH_fstring_key
     a = {"ABC" => :t}
     b = {"ABC" => :t}
@@ -946,7 +960,7 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L960
     h = @cls[]
     h.compare_by_identity
     h["a"] = 1
-    h["a"] = 2
+    h["a".dup] = 2
     assert_equal(["a",1], h.assoc("a"))
   end
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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