Speed up hash literals by duping
This commit replaces the `newhashfromarray` instruction with a `duphash` instruction. Instead of allocating a new hash from an array stored in the Instruction Sequences, store a hash directly in the instruction sequences and dup it on execution. == Instruction sequence changes == ```ruby code = <<-eorby { "foo" => "bar", "baz" => "lol" } eorby insns = RubyVM::InstructionSequence.compile(code, __FILE__, nil, 0, frozen_string_literal: true) puts insns.disasm ``` On Ruby 2.5: ``` == disasm: #<ISeq:<compiled>@test.rb:0 (0,0)-(0,36)>==================== 0000 putobject "foo" 0002 putobject "bar" 0004 putobject "baz" 0006 putobject "lol" 0008 newhash 4 0010 leave ``` Ruby 2.6@r66174 3b6321083a2e3525da3b34d08a0b68bac094bd7f: ``` $ ./ruby test.rb == disasm: #<ISeq:<compiled>@test.rb:0 (0,0)-(0,36)> (catch: FALSE) 0000 newhashfromarray 2, ["foo", "bar", "baz", "lol"] 0003 leave ``` Ruby 2.6 + This commit: ``` $ ./ruby test.rb == disasm: #<ISeq:<compiled>@test.rb:0 (0,0)-(0,36)> (catch: FALSE) 0000 duphash {"foo"=>"bar", "baz"=>"lol"} 0002 leave ``` == Benchmark Results == Compared to 2.5.3: ``` $ make benchmark ITEM=hash_literal_small COMPARE_RUBY=/Users/aaron/.rbenv/versions/2.5.3/bin/ruby generating known_errors.inc known_errors.inc unchanged ./revision.h unchanged /Users/aaron/.rbenv/shims/ruby --disable=gems -rrubygems -I./benchmark/lib ./benchmark/benchmark-driver/exe/benchmark-driver \ --executables="compare-ruby::/Users/aaron/.rbenv/versions/2.5.3/bin/ruby -I.ext/common --disable-gem" \ --executables="built-ruby::./miniruby -I./lib -I. -I.ext/common -r./prelude --disable-gem" \ $(find ./benchmark -maxdepth 1 -name '*hash_literal_small*.yml' -o -name '*hash_literal_small*.rb' | sort) Calculating ------------------------------------- compare-ruby built-ruby hash_literal_small2 1.498 1.877 i/s - 1.000 times in 0.667581s 0.532656s hash_literal_small4 1.197 1.642 i/s - 1.000 times in 0.835375s 0.609160s hash_literal_small8 0.620 1.215 i/s - 1.000 times in 1.611638s 0.823090s Comparison: hash_literal_small2 built-ruby: 1.9 i/s compare-ruby: 1.5 i/s - 1.25x slower hash_literal_small4 built-ruby: 1.6 i/s compare-ruby: 1.2 i/s - 1.37x slower hash_literal_small8 built-ruby: 1.2 i/s compare-ruby: 0.6 i/s - 1.96x slower ``` Compared to r66255 ``` $ make benchmark ITEM=hash_literal_small COMPARE_RUBY=/Users/aaron/.rbenv/versions/ruby-trunk/bin/ruby generating known_errors.inc known_errors.inc unchanged ./revision.h unchanged /Users/aaron/.rbenv/shims/ruby --disable=gems -rrubygems -I./benchmark/lib ./benchmark/benchmark-driver/exe/benchmark-driver \ --executables="compare-ruby::/Users/aaron/.rbenv/versions/ruby-trunk/bin/ruby -I.ext/common --disable-gem" \ --executables="built-ruby::./miniruby -I./lib -I. -I.ext/common -r./prelude --disable-gem" \ $(find ./benchmark -maxdepth 1 -name '*hash_literal_small*.yml' -o -name '*hash_literal_small*.rb' | sort) Calculating ------------------------------------- compare-ruby built-ruby hash_literal_small2 1.567 1.831 i/s - 1.000 times in 0.638056s 0.546039s hash_literal_small4 1.298 1.652 i/s - 1.000 times in 0.770214s 0.605182s hash_literal_small8 0.873 1.216 i/s - 1.000 times in 1.145304s 0.822047s Comparison: hash_literal_small2 built-ruby: 1.8 i/s compare-ruby: 1.6 i/s - 1.17x slower hash_literal_small4 built-ruby: 1.7 i/s compare-ruby: 1.3 i/s - 1.27x slower hash_literal_small8 built-ruby: 1.2 i/s compare-ruby: 0.9 i/s - 1.39x slower ``` git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66258 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
00a3108096
commit
2f37847820
@ -3984,7 +3984,12 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro
|
|||||||
ADD_INSN1(ret, line, duparray, ary);
|
ADD_INSN1(ret, line, duparray, ary);
|
||||||
}
|
}
|
||||||
else { /* COMPILE_ARRAY_TYPE_HASH */
|
else { /* COMPILE_ARRAY_TYPE_HASH */
|
||||||
ADD_INSN2(ret, line, newhashfromarray, INT2FIX(RARRAY_LEN(ary)/2), ary);
|
VALUE hash;
|
||||||
|
|
||||||
|
hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
|
||||||
|
rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR_TRANSIENT(ary), hash);
|
||||||
|
iseq_add_mark_object_compile_time(iseq, hash);
|
||||||
|
ADD_INSN1(ret, line, duphash, hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
23
insns.def
23
insns.def
@ -454,6 +454,16 @@ duparray
|
|||||||
val = rb_ary_resurrect(ary);
|
val = rb_ary_resurrect(ary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* dup hash */
|
||||||
|
DEFINE_INSN
|
||||||
|
duphash
|
||||||
|
(VALUE hash)
|
||||||
|
()
|
||||||
|
(VALUE val)
|
||||||
|
{
|
||||||
|
val = rb_hash_dup(hash);
|
||||||
|
}
|
||||||
|
|
||||||
/* if TOS is an array expand, expand it to num objects.
|
/* if TOS is an array expand, expand it to num objects.
|
||||||
if the number of the array is less than num, push nils to fill.
|
if the number of the array is less than num, push nils to fill.
|
||||||
if it is greater than num, exceeding elements are dropped.
|
if it is greater than num, exceeding elements are dropped.
|
||||||
@ -514,19 +524,6 @@ newhash
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make new Hash object from (frozen) Array object */
|
|
||||||
DEFINE_INSN
|
|
||||||
newhashfromarray
|
|
||||||
(rb_num_t num, VALUE ary)
|
|
||||||
()
|
|
||||||
(VALUE hash)
|
|
||||||
// attr bool leaf = false; /* rb_hash_bulk_insert() can call methods. */
|
|
||||||
{
|
|
||||||
VM_ASSERT(num * 2 == (rb_num_t)RARRAY_LEN(ary));
|
|
||||||
hash = rb_hash_new_with_size(num);
|
|
||||||
rb_hash_bulk_insert(num * 2, RARRAY_CONST_PTR_TRANSIENT(ary), hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* put new Range object.(Range.new(low, high, flag)) */
|
/* put new Range object.(Range.new(low, high, flag)) */
|
||||||
DEFINE_INSN
|
DEFINE_INSN
|
||||||
newrange
|
newrange
|
||||||
|
@ -239,8 +239,8 @@ class TestJIT < Test::Unit::TestCase
|
|||||||
assert_compile_once('a = 1; { a: a }', result_inspect: '{:a=>1}', insns: %i[newhash])
|
assert_compile_once('a = 1; { a: a }', result_inspect: '{:a=>1}', insns: %i[newhash])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_compile_insn_newhashfromarray
|
def test_compile_insn_duphash
|
||||||
assert_compile_once('{ a: 1 }', result_inspect: '{:a=>1}', insns: %i[newhashfromarray])
|
assert_compile_once('{ a: 1 }', result_inspect: '{:a=>1}', insns: %i[duphash])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_compile_insn_newrange
|
def test_compile_insn_newrange
|
||||||
|
Loading…
x
Reference in New Issue
Block a user