[前][次][番号順一覧][スレッド一覧]

ruby-changes:54039

From: tenderlove <ko1@a...>
Date: Fri, 7 Dec 2018 03:28:25 +0900 (JST)
Subject: [ruby-changes:54039] tenderlove:r66258 (trunk): Speed up hash literals by duping

tenderlove	2018-12-07 03:28:21 +0900 (Fri, 07 Dec 2018)

  New Revision: 66258

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=66258

  Log:
    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
    ```

  Modified files:
    trunk/compile.c
    trunk/insns.def
    trunk/test/ruby/test_jit.rb
Index: test/ruby/test_jit.rb
===================================================================
--- test/ruby/test_jit.rb	(revision 66257)
+++ test/ruby/test_jit.rb	(revision 66258)
@@ -239,8 +239,8 @@ class TestJIT < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_jit.rb#L239
     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
Index: insns.def
===================================================================
--- insns.def	(revision 66257)
+++ insns.def	(revision 66258)
@@ -454,6 +454,16 @@ duparray https://github.com/ruby/ruby/blob/trunk/insns.def#L454
     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 https://github.com/ruby/ruby/blob/trunk/insns.def#L524
     }
 }
 
-/* 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
Index: compile.c
===================================================================
--- compile.c	(revision 66257)
+++ compile.c	(revision 66258)
@@ -3984,7 +3984,12 @@ compile_array(rb_iseq_t *iseq, LINK_ANCH https://github.com/ruby/ruby/blob/trunk/compile.c#L3984
 			    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 {

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]