summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-12-06 18:28:21 +0000
committertenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-12-06 18:28:21 +0000
commit2f3784782070e7c44ac59958031a38787f69f86d (patch)
tree0471c7a9a907986046f886f4832c92cd4d92cb26
parent00a3108096c1c893af17cd90e184be54b00b1d27 (diff)
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
-rw-r--r--compile.c7
-rw-r--r--insns.def23
-rw-r--r--test/ruby/test_jit.rb4
3 files changed, 18 insertions, 16 deletions
diff --git a/compile.c b/compile.c
index d97cf4e8c6..b9a17fde3e 100644
--- a/compile.c
+++ b/compile.c
@@ -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 {
diff --git a/insns.def b/insns.def
index efd5bb74a2..73628a3376 100644
--- a/insns.def
+++ b/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
diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb
index fdcb0726ef..7627b2c048 100644
--- a/test/ruby/test_jit.rb
+++ b/test/ruby/test_jit.rb
@@ -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