ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: eregontp@gmail.com
To: ruby-core@ruby-lang.org
Subject: [ruby-core:99596] [Ruby master Feature#17055] Allow suppressing uninitialized instance variable and method redefined verbose mode warnings
Date: Sat, 15 Aug 2020 12:23:55 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-87074.20200815122354.1604@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-17055.20200728220329.1604@ruby-lang.org

Issue #17055 has been updated by Eregon (Benoit Daloze).


I tried on TruffleRuby and there I can't see a significant difference (at least for the noplugin case):
https://gist.github.com/eregon/e552bf55d42ce128a9d89f41d57b637f

TruffleRuby uses inline caches for almost all metaprogramming operations.
And so TruffleRuby optimizes `instance_variable_set :@ivar, value` to be essentially no different than `@ivar = value` when the ivar name is always the same.

So this might mean a good way to speed this case and remove this difference is to have an inline cache for `instance_variable_set` on CRuby too.

Regarding method call overhead, I think this is biased by CRuby without JIT having no inlining.
The method call overhead is something I would expect any Ruby JIT to eventually remove to at least some degree (there is essentially no overhead in TruffleRuby when inlined, the same as if manually inlining the method).
I think MJIT can inline some pure-Ruby methods (not sure if in `master` yet or not).

If you wanted to optimize to the extreme for the CRuby interpreter and no JIT, then one could use tricks like [OptCarrot `--opt`](https://github.com/mame/optcarrot#optimized-mode), for instance in this case to generate a single `#init_with` method with all assignments.
I don't think that's a good idea though, and I hope it shows my point we shouldn't over-optimize for one specific way of running Ruby.

jeremyevans0 (Jeremy Evans) wrote in #note-13:
> > So, for this case, how about using `def foo; @foo ||= nil; end`? That would avoid the warning and still let you assign the `@ivar` lazily, no?
> 
> Then you need a method call instead of an ivar access, which is also slower.  It also would require more changes across the library.

Not necessarily, you could (manually) inline it if that's a concern (but as mentioned above, I think a JIT would easily inline it for you).
It's probably worth a method though if > 1 usage and a non-trivial default value.
I actually found a few of those in Sequel, all related to this benchmark:

```ruby
defined?(@new) ? @new : (@new = false)
@errors ||= errors_class.new
@changed_columns ||= []
@associations ||= {}
```
(these 4 are all in their own method)

Initializing those to `nil` is redundant in the benchmark.

I think the last 3 make a lot of sense to initialize lazily since they involve additional allocations and might never be used.

For the first one, it's too bad that `@new ||= false` will repetitively assign to `false`, but `defined?()` is indeed a workaround for that.

In other words, that approach is already used, works well on existing versions, and has no overhead. It's not very pretty, but this is a micro-optimization. For pretty/readable, eager initialization seems better.

----------------------------------------
Feature #17055: Allow suppressing uninitialized instance variable and method redefined verbose mode warnings
https://bugs.ruby-lang.org/issues/17055#change-87074

* Author: jeremyevans0 (Jeremy Evans)
* Status: Open
* Priority: Normal
----------------------------------------
These two verbose mode warnings are both fairly common and have good reasons why you would not want to warn about them in specific cases.  Not initializing instance variables to nil can be much better for performance, and redefining methods without removing the method first is the only safe approach in multi-threaded code.

There are reasons that you may want to issue verbose warnings by default in these cases.  For uninitialized instance variables, it helps catch typos. For method redefinition, it could alert you that a method already exists when you didn't expect it to, such as when a file is loaded multiple times when it should only be loaded once.

I propose we keep the default behavior the same, but offer the ability to opt-out of these warnings by defining methods.  For uninitialized instance variables in verbose mode, I propose we call `expected_uninitialized_instance_variable?(iv)` on the object.  If this method doesn't exist or returns false/nil, we issue the warning.  If the method exists and returns true, we suppress the warning.  Similarly, for redefined methods, we call `expected_redefined_method?(method_name)` on the class or module.  If the method doesn't exist or returns false/nil, we issue the warning.  If the method exists and returns true, we suppress the warning.

This approach allows high performance code (uninitialized instance variables) and safe code (redefining methods without removing) to work without verbose mode warnings.

I have implemented this support in a pull request: https://github.com/ruby/ruby/pull/3371

---Files--------------------------------
t.rb (5.59 KB)


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

  parent reply	other threads:[~2020-08-15 12:24 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-07-28 22:03 [ruby-core:99375] [Ruby master Feature#17055] Allow suppressing uninitialized instance variable and method redefined verbose mode warnings merch-redmine
2020-07-29  2:26 ` [ruby-core:99386] " shyouhei
2020-07-29  6:54 ` [ruby-core:99387] " kamipo
2020-07-29  9:17 ` [ruby-core:99388] " jean.boussier
2020-07-29 20:48 ` [ruby-core:99395] " eregontp
2020-07-29 20:55 ` [ruby-core:99396] " eregontp
2020-07-29 21:37 ` [ruby-core:99397] " merch-redmine
2020-07-30 18:04 ` [ruby-core:99400] " headius
2020-07-30 18:16 ` [ruby-core:99401] " headius
2020-08-01 14:36 ` [ruby-core:99439] " eregontp
2020-08-01 14:54 ` [ruby-core:99440] " eregontp
2020-08-02 15:38 ` [ruby-core:99445] " merch-redmine
2020-08-03 11:22 ` [ruby-core:99453] " eregontp
2020-08-03 16:30 ` [ruby-core:99457] " merch-redmine
2020-08-03 16:48 ` [ruby-core:99458] " tenderlove
2020-08-03 17:01 ` [ruby-core:99459] " merch-redmine
2020-08-03 18:31 ` [ruby-core:99460] " tenderlove
2020-08-04  2:59 ` [ruby-core:99472] " merch-redmine
2020-08-15 10:54 ` [ruby-core:99595] " eregontp
2020-08-15 12:23 ` eregontp [this message]
2020-08-15 15:42 ` [ruby-core:99597] " merch-redmine
2020-09-01 15:56 ` [ruby-core:99819] " merch-redmine
2020-09-02 14:57 ` [ruby-core:99844] " eregontp
2020-09-02 15:35 ` [ruby-core:99845] " merch-redmine
2020-12-02 21:25 ` [ruby-core:101207] " merch-redmine
2020-12-03 22:44   ` [ruby-core:101231] " Austin Ziegler
2020-12-03 23:04     ` [ruby-core:101233] " Jeremy Evans
2020-12-04 17:34       ` [ruby-core:101244] " Austin Ziegler
2020-12-10  4:35 ` [ruby-core:101353] " matz

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-87074.20200815122354.1604@ruby-lang.org \
    --to=ruby-core@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).