[#105882] [Ruby master Bug#18280] Segmentation Fault in rb_utf8_str_new_cstr(NULL) — "ukolovda (Dmitry Ukolov)" <noreply@...>

Issue #18280 has been reported by ukolovda (Dmitry Ukolov).

13 messages 2021/11/01

[#105897] [Ruby master Bug#18282] Rails CI raises Segmentation fault with ruby 3.1.0dev supporting `Class#descendants` — "yahonda (Yasuo Honda)" <noreply@...>

Issue #18282 has been reported by yahonda (Yasuo Honda).

12 messages 2021/11/02

[#105909] [Ruby master Misc#18285] NoMethodError#message uses a lot of CPU/is really expensive to call — "ivoanjo (Ivo Anjo)" <noreply@...>

Issue #18285 has been reported by ivoanjo (Ivo Anjo).

37 messages 2021/11/02

[#105920] [Ruby master Bug#18286] Universal arm64/x86_84 binary built on an x86_64 machine segfaults/is killed on arm64 — "ccaviness (Clay Caviness)" <noreply@...>

Issue #18286 has been reported by ccaviness (Clay Caviness).

16 messages 2021/11/03

[#105928] [Ruby master Feature#18287] Support nil value for sort in Dir.glob — "Strech (Sergey Fedorov)" <noreply@...>

Issue #18287 has been reported by Strech (Sergey Fedorov).

16 messages 2021/11/04

[#105944] [Ruby master Bug#18289] Enumerable#to_a should delegate keyword arguments to #each — "Ethan (Ethan -)" <noreply@...>

Issue #18289 has been reported by Ethan (Ethan -).

8 messages 2021/11/05

[#105967] [Ruby master Bug#18293] Time.at in master branch was 25% slower then Ruby 3.0 — "watson1978 (Shizuo Fujita)" <noreply@...>

Issue #18293 has been reported by watson1978 (Shizuo Fujita).

17 messages 2021/11/08

[#106008] [Ruby master Bug#18296] Custom exception formatting should override `Exception#full_message`. — "ioquatix (Samuel Williams)" <noreply@...>

Issue #18296 has been reported by ioquatix (Samuel Williams).

14 messages 2021/11/10

[#106033] [Ruby master Bug#18330] Make failure on 32-bit Linux (Android) with Clang due to implicit 64-to-32-bit integer truncation — "xtkoba (Tee KOBAYASHI)" <noreply@...>

Issue #18330 has been reported by xtkoba (Tee KOBAYASHI).

10 messages 2021/11/11

[#106053] [Ruby master Misc#18335] openindiana ruby 3.1 preview needs --disable-dtrace — "stes (David Stes)" <noreply@...>

Issue #18335 has been reported by stes (David Stes).

14 messages 2021/11/14

[#106069] [Ruby master Feature#18339] GVL instrumentation API — "byroot (Jean Boussier)" <noreply@...>

Issue #18339 has been reported by byroot (Jean Boussier).

13 messages 2021/11/15

[#106145] [Ruby master Misc#18346] DevelopersMeeting20211209Japan — "mame (Yusuke Endoh)" <noreply@...>

Issue #18346 has been reported by mame (Yusuke Endoh).

11 messages 2021/11/18

[#106173] [Ruby master Feature#18349] Let --jit enable YJIT — "k0kubun (Takashi Kokubun)" <noreply@...>

Issue #18349 has been reported by k0kubun (Takashi Kokubun).

8 messages 2021/11/19

[#106175] [Ruby master Feature#18351] Support anonymous rest and keyword rest argument forwarding — "jeremyevans0 (Jeremy Evans)" <noreply@...>

Issue #18351 has been reported by jeremyevans0 (Jeremy Evans).

10 messages 2021/11/19

[#106279] [Ruby master Feature#18364] Add GC.stat_size_pool for Variable Width Allocation — "peterzhu2118 (Peter Zhu)" <noreply@...>

Issue #18364 has been reported by peterzhu2118 (Peter Zhu).

14 messages 2021/11/25

[#106308] [Ruby master Feature#18367] Stop the interpreter from escaping error messages — "mame (Yusuke Endoh)" <noreply@...>

Issue #18367 has been reported by mame (Yusuke Endoh).

13 messages 2021/11/29

[#106314] [Ruby master Feature#18368] Range#step semantics for non-Numeric ranges — "zverok (Victor Shepelev)" <noreply@...>

Issue #18368 has been reported by zverok (Victor Shepelev).

39 messages 2021/11/29

[#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

[#106347] [Ruby master Feature#18370] Call Exception#full_message to print exceptions reaching the top-level — "Eregon (Benoit Daloze)" <noreply@...>

Issue #18370 has been reported by Eregon (Benoit Daloze).

10 messages 2021/11/30

[ruby-core:105915] [Ruby master Misc#18285] NoMethodError#message uses a lot of CPU/is really expensive to call

From: "ivoanjo (Ivo Anjo)" <noreply@...>
Date: 2021-11-03 09:15:01 UTC
List: ruby-core #105915
Issue #18285 has been updated by ivoanjo (Ivo Anjo).


#### Important note! I'm actually talking about **`NoMethodError`**, not **`NameError`**, so I don't think this issue is caused by the did you mean gem behavior (which I really like).

This may have been confusing because both of them share the `NameError::message` class where the actual call to `#inspect` lives.

> Let me confirm: you are facing this issue when testing dd-trace-rb, not in production, right? I cannot imagine how NameError#message is called so many times in production.

Apologies for the confusion. I meant that we were simulating what if a production Rails application had a bug (in this case, mistakenly called a method that had been deleted), and how dd-trace-rb would help customers debug such an issue.

I think it's reasonable assertion to say this can happen -- people will do these kinds of mistakes, and if they're not  paying attention to their logs/alarms/error tracking data, their app can be impacted by such an issue for a while until they realize what's going on and have the chance to deploy a fix.

---

In my view, part of the problem here is that on classes with a big `#inspect`, the call to `#inspect` ends up being completely useless on Ruby 2.7 and below:


```ruby
> puts RUBY_DESCRIPTION
ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x86_64-darwin19]
> class Foo; def inspect; puts 'inspect called!'; 'A' * 65; end; end
> Foo.new.foo
inspect called!
# => NoMethodError: undefined method `foo' for AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:Foo
> class Foo; def inspect; puts 'inspect called!'; 'A' * 66; end; end
> Foo.new.foo
inspect called!
# => NoMethodError: undefined method `foo' for #<Foo:0x00007f875719b140>
```

When I was testing, I wasn't seeing the result of the `#inspect`, so it wasn't even occurring to me what was going on (and this is what confused me even further).

I realize now that the limit was removed in 3.0 (https://bugs.ruby-lang.org/issues/16832 and https://github.com/ruby/ruby/pull/3090) so in 3.0 it always shows the whole string, which can be a whole another problem as well, because people may not be expecting a `NoMethodError` to contain a small novel in its `#message`.

So what I'm asking upstream to consider is:

*Could we simplify `NoMethodError` to not include the `#inspect`? According to the PR above, 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.*


----------------------------------------
Misc #18285: NoMethodError#message uses a lot of CPU/is really expensive to call
https://bugs.ruby-lang.org/issues/18285#change-94458

* 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:[email protected]?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread