ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: "shugo (Shugo Maeda)" <noreply@ruby-lang.org>
To: ruby-core@ml.ruby-lang.org
Subject: [ruby-core:111198] [Ruby master Bug#19165] Method (with no param) delegation with *, **, and ... is slow
Date: Sun, 04 Dec 2022 07:14:30 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-100481.20221204071429.94@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-19165.20221201084916.94@ruby-lang.org

Issue #19165 has been updated by shugo (Shugo Maeda).





It seems that `...` is faster without [Feature #19134]:



```

         simple call     13.250M (± 2.0%) i/s -     66.792M in   5.043180s

delegate without splat

                         12.523M (± 1.3%) i/s -     62.863M in   5.020866s

 delegate with splat      6.231M (± 1.8%) i/s -     31.452M in   5.049532s

delegate with splat with name

                          6.152M (± 3.3%) i/s -     30.958M in   5.038120s

delegate with splat and double splat

                          2.187M (± 2.0%) i/s -     10.981M in   5.024101s

delegate with triple dots

                          5.976M (± 1.6%) i/s -     30.120M in   5.041456s

delegate via forwardable

                          5.072M (± 1.4%) i/s -     25.818M in   5.091690s

```



`args = arg_append(p, args, new_hash(p, kwrest, loc), loc);` in the following code seems to be slow.



```

static NODE *

new_args_forward_call(struct parser_params *p, NODE *leading, const YYLTYPE *loc, const YYLTYPE *argsloc)

{

    NODE *rest = NEW_LVAR(idFWD_REST, loc);

    NODE *kwrest = list_append(p, NEW_LIST(0, loc), NEW_LVAR(idFWD_KWREST, loc));

    NODE *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, loc), loc);

    NODE *args = leading ? rest_arg_append(p, leading, rest, loc) : NEW_SPLAT(rest, loc);

    args = arg_append(p, args, new_hash(p, kwrest, loc), loc);

    return arg_blk_pass(args, block);

}

```



Should we revert [Feature #19134]?







----------------------------------------

Bug #19165: Method (with no param) delegation with *, **, and ... is slow

https://bugs.ruby-lang.org/issues/19165#change-100481



* Author: matsuda (Akira Matsuda)

* Status: Open

* Priority: Normal

* ruby -v: ruby 3.2.0dev (2022-12-01T08:05:41Z master 4e68b59431) +YJIT [arm64-darwin21]

* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN

----------------------------------------

I found that method delegation via Forwardable is much slower than normal method call when delegating a method that does not take parameters.



Here's a benchmark that explains what I mean.



```

require 'forwardable'

require 'pp'

require 'benchmark/ips'



class Obj

  extend Forwardable



  attr_accessor :other



  def initialize

    @other = Other.new

  end



  def foo_without_splat

    @other.foo

  end



  def foo_with_splat(*)

    @other.foo(*)

  end



  def foo_with_splat_with_name(*args)

    @other.foo(*args)

  end



  def foo_with_splat_and_double_splat(*, **)

    @other.foo(*, **)

  end



  def foo_with_triple_dots(...)

    @other.foo(...)

  end



  delegate :foo => :@other

end



class Other

  def foo() end

end



o = Obj.new



Benchmark.ips do |x|

  x.report 'simple call' do

    o.other.foo

  end



  x.report 'delegate without splat' do

    o.foo_without_splat

  end



  x.report 'delegate with splat' do

    o.foo_with_splat

  end



  x.report 'delegate with splat with name' do

    o.foo_with_splat_with_name

  end



  x.report 'delegate with splat and double splat' do

    o.foo_with_splat_and_double_splat

  end



  x.report 'delegate with triple dots' do

    o.foo_with_triple_dots

  end



  x.report 'delegate via forwardable' do

    o.foo

  end

end





(result)

         simple call     38.918M (± 0.9%) i/s -    194.884M

delegate without splat

                         31.933M (± 1.6%) i/s -    159.611M

 delegate with splat     10.269M (± 1.6%) i/s -     51.631M

delegate with splat with name

                          9.888M (± 1.0%) i/s -     49.588M

delegate with splat and double splat

                          4.117M (± 0.9%) i/s -     20.696M

delegate with triple dots

                          4.169M (± 0.9%) i/s -     20.857M

delegate via forwardable

                          9.204M (± 2.1%) i/s -     46.295M

```



It shows that Method delegation with a splat is 3-4 times slower (regardless of whether the parameter is named or not), and delegation with a triple-dot literal is 9-10 times slower than a method delegation without an argument.

This may be because calling a method taking a splat always assigns an Array object even when no actual argument was given, and calling a method taking triple-dots assigns five Array objects and two Hash objects (this is equivalent to `*, **`).





Are there any chance reducing these object assignments and making them faster? My concern is that the Rails framework heavily uses this kind of method delegations, and presumably it causes unignorable performance overhead.







-- 

https://bugs.ruby-lang.org/

 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

  parent reply	other threads:[~2022-12-04  7:14 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-01  8:49 [ruby-core:111121] [Ruby master Bug#19165] Method (with no param) delegation with *, **, and ... is slow matsuda (Akira Matsuda)
2022-12-01  8:55 ` [ruby-core:111124] " matsuda (Akira Matsuda)
2022-12-01 22:06 ` [ruby-core:111137] " Eregon (Benoit Daloze)
2022-12-04  7:14 ` shugo (Shugo Maeda) [this message]
2022-12-13 19:01 ` [ruby-core:111275] " ko1 (Koichi Sasada)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-100481.20221204071429.94@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    --cc=ruby-core@ml.ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).