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 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@44551 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
9bfaeffd53
commit
58f800a278
12
ChangeLog
12
ChangeLog
@ -1,3 +1,15 @@
|
|||||||
|
Fri Jan 10 13:34:04 2014 Aman Gupta <ruby@tmm1.net>
|
||||||
|
|
||||||
|
* 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@gmail.com>
|
Fri Jan 10 06:23:21 2014 Benoit Daloze <eregontp@gmail.com>
|
||||||
|
|
||||||
* range.c (Range#size): [DOC] improve description and add examples.
|
* range.c (Range#size): [DOC] improve description and add examples.
|
||||||
|
39
compile.c
39
compile.c
@ -4319,6 +4319,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NODE_CALL:
|
case NODE_CALL:
|
||||||
|
/* optimization shortcut
|
||||||
|
* "literal".freeze -> opt_str_freeze("literal")
|
||||||
|
*/
|
||||||
if (node->nd_recv && nd_type(node->nd_recv) == NODE_STR &&
|
if (node->nd_recv && nd_type(node->nd_recv) == NODE_STR &&
|
||||||
node->nd_mid == idFreeze && node->nd_args == NULL)
|
node->nd_mid == idFreeze && node->nd_args == NULL)
|
||||||
{
|
{
|
||||||
@ -4330,6 +4333,23 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
|
|||||||
}
|
}
|
||||||
break;
|
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_FCALL:
|
||||||
case NODE_VCALL:{ /* VCALL: variable or call */
|
case NODE_VCALL:{ /* VCALL: variable or call */
|
||||||
/*
|
/*
|
||||||
@ -5300,6 +5320,25 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
|
|||||||
VALUE flag = 0;
|
VALUE flag = 0;
|
||||||
VALUE argc;
|
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(recv);
|
||||||
INIT_ANCHOR(args);
|
INIT_ANCHOR(args);
|
||||||
argc = setup_args(iseq, args, node->nd_args, &flag);
|
argc = setup_args(iseq, args, node->nd_args, &flag);
|
||||||
|
2
hash.c
2
hash.c
@ -2399,7 +2399,7 @@ static VALUE rb_hash_compare_by_id_p(VALUE hash);
|
|||||||
* h1["a"] #=> 100
|
* h1["a"] #=> 100
|
||||||
* h1.compare_by_identity
|
* h1.compare_by_identity
|
||||||
* h1.compare_by_identity? #=> true
|
* h1.compare_by_identity? #=> true
|
||||||
* h1["a"] #=> nil # different objects.
|
* h1["a".dup] #=> nil # different objects.
|
||||||
* h1[:c] #=> "c" # same symbols are all same.
|
* h1[:c] #=> "c" # same symbols are all same.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
41
insns.def
41
insns.def
@ -1901,6 +1901,47 @@ opt_aset
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@c optimize
|
||||||
|
@e recv[str] = set
|
||||||
|
@j 最適化された 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 最適化された 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
|
@c optimize
|
||||||
@e optimized length
|
@e optimized length
|
||||||
|
@ -209,6 +209,20 @@ class TestHash < Test::Unit::TestCase
|
|||||||
assert_equal(256, h[z])
|
assert_equal(256, h[z])
|
||||||
end
|
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
|
def test_NEWHASH_fstring_key
|
||||||
a = {"ABC" => :t}
|
a = {"ABC" => :t}
|
||||||
b = {"ABC" => :t}
|
b = {"ABC" => :t}
|
||||||
@ -946,7 +960,7 @@ class TestHash < Test::Unit::TestCase
|
|||||||
h = @cls[]
|
h = @cls[]
|
||||||
h.compare_by_identity
|
h.compare_by_identity
|
||||||
h["a"] = 1
|
h["a"] = 1
|
||||||
h["a"] = 2
|
h["a".dup] = 2
|
||||||
assert_equal(["a",1], h.assoc("a"))
|
assert_equal(["a",1], h.assoc("a"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user