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/