[#106341] [Ruby master Bug#18369] users.detect(:name, "Dorian") as shorthand for users.detect { |user| user.name == "Dorian" } — dorianmariefr <noreply@...>
Issue #18369 has been reported by dorianmariefr (Dorian Mari辿).
14 messages
2021/11/30
[#106351] [Ruby master Bug#18371] Release branches (release information in general) — "tenderlovemaking (Aaron Patterson)" <noreply@...>
Issue #18371 has been reported by tenderlovemaking (Aaron Patterson).
7 messages
2021/11/30
[ruby-core:105916] [Ruby master Misc#18285] NoMethodError#message uses a lot of CPU/is really expensive to call
From:
"byroot (Jean Boussier)" <noreply@...>
Date:
2021-11-03 11:23:04 UTC
List:
ruby-core #105916
Issue #18285 has been updated by byroot (Jean Boussier).
> I'm actually talking about NoMethodError, not NameError, so I don't think this issue is caused by the did you mean gem behavior
No the confusion is mine, I assumed it was a `did_you_mean` perf issue without fully digging into your repro because I ran into those previously.
> Could we simplify NoMethodError to not include the #inspect?
I'm personally not against it, e.g:
```
>> {foo: 42}.bar
(irb):2:in `<main>': undefined method `bar' for {:foo=>42}:Hash (NoMethodError)
```
The content of the hash here isn't really helpful, in the context of a `NoMethodError` all I really care about is the type of the object, so why not. I might be missing some use cases though.
> the previous limit was in place for about 24 years and it looks like nobody really complained that they couldn't see their #inspect on objects which had a complex one.
Well I for one complained about that limit, hence why I opened the ticket.
Also note that the limit would still have called `#inspect`, just not used the result, so unless I'm missing something, you could truncate `Error#message` yourself and have roughly the same performance.
And in the context of a library reporting error, you might have to deal with arbitrary errors raised by the application code, so you can't assume all errors will have a reasonably small `message` anyway.
----------------------------------------
Misc #18285: NoMethodError#message uses a lot of CPU/is really expensive to call
https://bugs.ruby-lang.org/issues/18285#change-94459
* Author: ivoanjo (Ivo Anjo)
* Status: Open
* Priority: Normal
----------------------------------------
Hello there! I'm working at Datadog on the ddtrace gem -- https://github.com/DataDog/dd-trace-rb and we ran into this issue on one of our internal testing applications. I also blogged about this issue in <https://ivoanjo.me/blog/2021/11/01/nomethoderror-ruby-cost/>.
### Background
While testing an application that threw a lot of `NoMethodError`s in a Rails controller (this was used for validation), we discovered that service performance was very much impacted when we were logging these exceptions. While investigating with a profiler, the performance impact was caused by calls to `NoMethodError#message`, because this Rails controller had a quite complex `#inspect` method, that was getting called every time we tried to get the `#message` from the exception.
### How to reproduce
```ruby
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'benchmark-ips'
end
puts RUBY_DESCRIPTION
class GemInformation
# ...
def get_no_method_error
method_does_not_exist
rescue => e
e
end
def get_runtime_error
raise 'Another Error'
rescue => e
e
end
def inspect # <-- expensive method gets called when calling NoMethodError#message
Gem::Specification._all.inspect
end
end
NO_METHOD_ERROR_INSTANCE = GemInformation.new.get_no_method_error
RUNTIME_ERROR_INSTANCE = GemInformation.new.get_runtime_error
Benchmark.ips do |x|
x.config(:time => 5, :warmup => 2)
x.report("no method error message cost") { NO_METHOD_ERROR_INSTANCE.message }
x.report("runtime error message cost") { RUNTIME_ERROR_INSTANCE.message }
x.compare!
end
```
### Expectation and result
Getting the `#message` from a `NoMethodError` should be no costly than getting it from any other exception.
In reality:
```
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux]
no method error message cost
115.390 (賊 1.7%) i/s - 580.000 in 5.027822s
runtime error message cost
6.938M (賊 0.5%) i/s - 35.334M in 5.092617s
Comparison:
runtime error message cost: 6938381.6 i/s
no method error message cost: 115.4 i/s - 60130.02x (賊 0.00) slower
```
### Suggested solutions
1. Do not call `#inspect` on the object on which the method was not found (see <https://github.com/ruby/ruby/blob/e0915ba67964d843832148aeca29a1f8244ca7b1/error.c#L1962>)
2. Cache result of calling `#message` after the first call. Ideally this should be done together with suggestion 1.
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>