1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Skip defined check in NODE_OP_ASGN_OR with ivar

Previously we would add code to check if an ivar was defined when using
`@foo ||= 123`, which was slower than `@foo || (@foo = 123)` when `@foo`
was already defined.

Recently 01b7d5acc7 made it so that
accessing an undefined variable no longer generates a warning, making
the defined check unnecessary and both statements exactly equal.

This commit avoids emitting the defined instruction when compiling
NODE_OP_ASGN_OR with a NODE_IVAR.

Before:

    $ ruby --dump=insn -e '@foo ||= 123'
    == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,12)> (catch: FALSE)
    0000 putnil                                                           (   1)[Li]
    0001 defined                      instance-variable, :@foo, false
    0005 branchunless                 14
    0007 getinstancevariable          :@foo, <is:0>
    0010 dup
    0011 branchif                     20
    0013 pop
    0014 putobject                    123
    0016 dup
    0017 setinstancevariable          :@foo, <is:0>
    0020 leave

After:

    $ ./ruby --dump=insn -e '@foo ||= 123'
    == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,12)> (catch: FALSE)
    0000 getinstancevariable                    :@foo, <is:0>             (   1)[Li]
    0003 dup
    0004 branchif                               13
    0006 pop
    0007 putobject                              123
    0009 dup
    0010 setinstancevariable                    :@foo, <is:0>
    0013 leave

This seems to be about 50% faster in this benchmark:

    require "benchmark/ips"

    class Foo
      def initialize
        @foo = nil
      end

      def test1
        @foo ||= 123
      end

      def test2
        @foo || (@foo = 123)
      end
    end

    FOO = Foo.new

    Benchmark.ips do |x|
      x.report("test1", "FOO.test1")
      x.report("test2", "FOO.test2")
    end

Before:

    $ ruby benchmark_ivar.rb
    Warming up --------------------------------------
                   test1     1.957M i/100ms
                   test2     3.125M i/100ms
    Calculating -------------------------------------
                   test1     20.030M (± 1.7%) i/s -    101.780M in   5.083040s
                   test2     31.227M (± 4.5%) i/s -    156.262M in   5.015936s

After:

    $ ./ruby benchmark_ivar.rb
    Warming up --------------------------------------
                   test1     3.205M i/100ms
                   test2     3.197M i/100ms
    Calculating -------------------------------------
                   test1     32.066M (± 1.1%) i/s -    163.440M in   5.097581s
                   test2     31.438M (± 4.9%) i/s -    159.860M in   5.098961s
This commit is contained in:
John Hawthorn 2020-12-14 17:34:31 -08:00 committed by Jeremy Evans
parent 2fa9f3c032
commit 40b7358e93
Notes: git 2020-12-15 12:39:24 +09:00

View file

@ -8024,7 +8024,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
LABEL *lfin = NEW_LABEL(line);
LABEL *lassign;
if (nd_type(node) == NODE_OP_ASGN_OR) {
if (nd_type(node) == NODE_OP_ASGN_OR && nd_type(node->nd_head) != NODE_IVAR) {
LABEL *lfinish[2];
lfinish[0] = lfin;
lfinish[1] = 0;