mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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 3b6321083a
:
```
$ ./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
3 changed files with 18 additions and 16 deletions
|
@ -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);
|
||||
}
|
||||
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 {
|
||||
|
|
23
insns.def
23
insns.def
|
@ -454,6 +454,16 @@ duparray
|
|||
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 the number of the array is less than num, push nils to fill.
|
||||
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)) */
|
||||
DEFINE_INSN
|
||||
newrange
|
||||
|
|
|
@ -239,8 +239,8 @@ class TestJIT < Test::Unit::TestCase
|
|||
assert_compile_once('a = 1; { a: a }', result_inspect: '{:a=>1}', insns: %i[newhash])
|
||||
end
|
||||
|
||||
def test_compile_insn_newhashfromarray
|
||||
assert_compile_once('{ a: 1 }', result_inspect: '{:a=>1}', insns: %i[newhashfromarray])
|
||||
def test_compile_insn_duphash
|
||||
assert_compile_once('{ a: 1 }', result_inspect: '{:a=>1}', insns: %i[duphash])
|
||||
end
|
||||
|
||||
def test_compile_insn_newrange
|
||||
|
|
Loading…
Reference in a new issue