* [ruby-core:111121] [Ruby master Bug#19165] Method (with no param) delegation with *, **, and ... is slow
@ 2022-12-01 8:49 matsuda (Akira Matsuda)
2022-12-01 8:55 ` [ruby-core:111124] " matsuda (Akira Matsuda)
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: matsuda (Akira Matsuda) @ 2022-12-01 8:49 UTC (permalink / raw)
To: ruby-core
Issue #19165 has been reported by matsuda (Akira Matsuda).
----------------------------------------
Bug #19165: Method (with no param) delegation with *, **, and ... is slow
https://bugs.ruby-lang.org/issues/19165
* 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/
^ permalink raw reply [flat|nested] 5+ messages in thread
* [ruby-core:111124] [Ruby master Bug#19165] Method (with no param) delegation with *, **, and ... is slow
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 ` matsuda (Akira Matsuda)
2022-12-01 22:06 ` [ruby-core:111137] " Eregon (Benoit Daloze)
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: matsuda (Akira Matsuda) @ 2022-12-01 8:55 UTC (permalink / raw)
To: ruby-core
Issue #19165 has been updated by matsuda (Akira Matsuda).
FYI for confirming "five Array objects and two Hash objects" that I wrote above, I used ko1's allocation_tracer as follows:
```
require 'allocation_tracer'
ObjectSpace::AllocationTracer.setup([:type])
o.foo_with_triple_dots
pp ObjectSpace::AllocationTracer.trace {
o.foo_with_triple_dots
}
```
----------------------------------------
Bug #19165: Method (with no param) delegation with *, **, and ... is slow
https://bugs.ruby-lang.org/issues/19165#change-100398
* 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/
^ permalink raw reply [flat|nested] 5+ messages in thread
* [ruby-core:111137] [Ruby master Bug#19165] Method (with no param) delegation with *, **, and ... is slow
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 ` Eregon (Benoit Daloze)
2022-12-04 7:14 ` [ruby-core:111198] " shugo (Shugo Maeda)
2022-12-13 19:01 ` [ruby-core:111275] " ko1 (Koichi Sasada)
3 siblings, 0 replies; 5+ messages in thread
From: Eregon (Benoit Daloze) @ 2022-12-01 22:06 UTC (permalink / raw)
To: ruby-core
Issue #19165 has been updated by Eregon (Benoit Daloze).
How many allocations is it with `...` when not using forwardable but just delegation with `(...)`? I'd think 1 Array + 1 Hash.
----------------------------------------
Bug #19165: Method (with no param) delegation with *, **, and ... is slow
https://bugs.ruby-lang.org/issues/19165#change-100416
* 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/
^ permalink raw reply [flat|nested] 5+ messages in thread
* [ruby-core:111198] [Ruby master Bug#19165] Method (with no param) delegation with *, **, and ... is slow
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)
2022-12-13 19:01 ` [ruby-core:111275] " ko1 (Koichi Sasada)
3 siblings, 0 replies; 5+ messages in thread
From: shugo (Shugo Maeda) @ 2022-12-04 7:14 UTC (permalink / raw)
To: ruby-core
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/
^ permalink raw reply [flat|nested] 5+ messages in thread
* [ruby-core:111275] [Ruby master Bug#19165] Method (with no param) delegation with *, **, and ... is slow
2022-12-01 8:49 [ruby-core:111121] [Ruby master Bug#19165] Method (with no param) delegation with *, **, and ... is slow matsuda (Akira Matsuda)
` (2 preceding siblings ...)
2022-12-04 7:14 ` [ruby-core:111198] " shugo (Shugo Maeda)
@ 2022-12-13 19:01 ` ko1 (Koichi Sasada)
3 siblings, 0 replies; 5+ messages in thread
From: ko1 (Koichi Sasada) @ 2022-12-13 19:01 UTC (permalink / raw)
To: ruby-core
Issue #19165 has been updated by ko1 (Koichi Sasada).
I made a patch https://github.com/ruby/ruby/pull/6920
This patch improves the performance of the cases which are discussed on this ticket.
However, this patch changes the calling convention and YJIT and MJIT are needed to catch up.
I'm not sure we can do it in some days.
----------------------------------------
Bug #19165: Method (with no param) delegation with *, **, and ... is slow
https://bugs.ruby-lang.org/issues/19165#change-100627
* 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/
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2022-12-13 19:01 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [ruby-core:111198] " shugo (Shugo Maeda)
2022-12-13 19:01 ` [ruby-core:111275] " ko1 (Koichi Sasada)
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).