Project

General

Profile

Feature #11934

A feature to drop dynamics dynamically

Added by mame (Yusuke Endoh) over 3 years ago. Updated over 3 years ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:72640]

Description

Ruby is a dynamic language. Everything is possible in runtime.

So, how about a feature to prohibit some dynamic features in runtime?

Foo = 1

RubyVM.drop_dynamics

Bar = 2        #=> cannot define new constant
Foo = 2        #=> cannot redefine a constant
def foo; end   #=> cannot define a method
class Baz; end #=> cannot create a new class

Ruby's dynamic property greatly restricts performance. However, it is a bad idea to limit Ruby's dynamics. It is Ruby's identity, and is actually useful in some aspects such as debugging, daily-scripting, research, hobby-use, etc.

That being said, when a program is in production, we don't necessarily use the dynamic features at any time during execution. Typically, they are used only in initialization of application, such as eval-for-DRY and monkey patching. After the initialization is done, we can drop (some of) dynamic features in some situations.

Currently, when considering optimization of Ruby implementation, we (the core-team) must always care the possibility of redefinition of any built-in methods. That is not sound. This proposal gives us a "normal" condition for considering optimization. We can easily apply a lot of optimizations, like constant folding and method inline expansion. In addition, some advanced analyses like JIT/AOT compilation, type inference and whole program optimization will be applicable much more effectively.

--

It is arguable what features are prohibited. IMO, it is relatively easy to drop the following features.

  • (re)definition of constants
  • (re)definition of methods (including singleton methods an aliases)
  • (re)definition of classes/modules
  • inclusion of modules

Aggressively, we may limit the following features.

  • destructive modification of some kind of objects (such as making string literal frozen by default)
  • some meta-programming features like Object#send
  • eval and instance_eval
  • addition of instance variables
  • XXX <- feel free to add your unfavorite features

We should care about a trade-off between compatibility impact and optimization effect.

--

I'm half-joking, but half-serious. I'm unsure if this is really a great idea, but surely better than just restricting Ruby specification by default (such as frozen string literal). This may be a key feature towards Ruby3x3.

What do you think?

--
Yusuke Endoh mame@ruby-lang.org

History

Updated by normalperson (Eric Wong) over 3 years ago

mame@ruby-lang.org wrote:

So, how about a feature to prohibit some dynamic features in runtime?

We already allow "freeze" on classes and modules; perhaps we perform
some optimizations when they become frozen: perfect hashing,
rehash/repack hashes for locality, reducing checks in bytecode, etc...

That should be both backwards and forwards compatible with
old/parallel/future Ruby implementations and have better granularity
instead of the whole VM.

Updated by mame (Yusuke Endoh) over 3 years ago

Eric, thank you for your comment.

We already allow "freeze" on classes and modules; perhaps we perform
some optimizations when they become frozen: perfect hashing,
rehash/repack hashes for locality, reducing checks in bytecode, etc...

Is constant folding possible? And method inlining? They may be possible, but I guess the effect will be restricted. To get the best of the optimizations, I think we need to freeze all classes and modules, including singleton classes.

Personally I don't want to recommend "freeze on classes/modules" because some people may use them blindly, like "string".freeze. If it is widely used by default in many libraries for the purpose of performance improvement, Ruby's dynamics may be killed in effect. Instead, switching on the whole VM looks a better choice to me.

In addition, it would be good to include "frozen string literal by default" into this mode. It is impossible by "freeze on classes/modules".

That should be both backwards and forwards compatible with
old/parallel/future Ruby implementations and have better granularity
instead of the whole VM.

Compatibility will be kept perfectly unless this feature is used. In fact, this is one of the motivation of this proposal. Recently, Ruby tends to sacrifice dynamics for performance improvement (such as frozen string literal by default). I'd like to stop this and provide better "playing field" for users and developers who are interested in optimization.

--
Yusuke Endoh mame@ruby-lang.org

Updated by chrisseaton (Chris Seaton) over 3 years ago

All of the dynamic language features described (constant and method redefinition, inclusion of modules etc) can already be done with no runtime cost (http://chrisseaton.com/rubytruffle/deoptimizing/, http://chrisseaton.com/phd/). If these techniques were implemented, drop_dynamics should make no difference to performance.

Now, that does require a JIT and a more sophisticated VM, but if the idea is to disable the language features in order to make that task easier then it's kind of a circular argument - disabling these features to make it easier to add a JIT, add inlining etc, but if we had a JIT then we wouldn't need to disable the features in the first place.

We could still argue that even if we don't add a JIT or new optimisations such as method inlining, then disabling these features will improve the performance of MRI. I'm not so sure that's the case (but neither of us have empirical data on that). The check for redefinition is just reading and comparing a word isn't it? Compared to the massive overhead of bytecode interpretation, frames allocated on the heap etc, I'm not sure that reading a word is really a big problem.

Philosophically, I feel like it's the VM's job to fix performance, and we shouldn't be passing the buck to the user with things like drop_dynamics. If we did this we wouldn't be making Ruby 3x faster - we'd be making something that looked like Ruby but wasn't dynamic run faster.

Updated by Eregon (Benoit Daloze) over 3 years ago

I am not sure this would bring much in terms of performance,
and it would definitely limits lots of stuff to run in this mode (Rails as today would certainly not).
It sounds a bit to me like trying another language.

Constant folding and method inlining could already be performed if the guard for the constant/method lookup is carried along when inlining the method/constant access.

Whole-program optimizations like type inference could figure out more stuff but it seems very hard in Ruby even with these limitations to prove the exact single type of most expressions.
And then, for instance for method inlining, this would be just redundant with the data already available in inline caches, which a JIT could already take advantage of.

In light of the recent discussions about OpenStruct, it also feels that reflection and meta-programming should be made faster and not prohibited.
The complexity of the current version of OpenStruct is quite high as the related bugs show, with all the smart tricks instead of a fast method_missing.

Updated by shevegen (Robert A. Heiler) over 3 years ago

It would be nice to fine-tune some of ruby's behaviour like that.

So I am encouraging all ideas similar as to the above.

There is only one concern I have - is it possible to become
"less" strict again that is, to revert to the default MRI
behaviour?

RubyVM.drop_dynamics
RubyVM.enable_dynamics

Also I am not sure if RubyVM is a good namespace for this,
perhaps this is more RbConfig or some other name? But the
name is not so important, the functionality is more important,
the name can easily be decided. Deciding on functionality
is more difficult.

Thanks for reading!

Updated by justcolin (Colin Fulton) over 3 years ago

I agree with Chris: JRuby + Truffle showed that it is possible to have dynamic features without loosing performance. Philosophically I am reminded of Ruby's syntax. It is really hard to write a Ruby parser but it that hard to parse syntax makes it easier for developers to write easy to read code.

A de-optimizing JIT compiler like the one JRuby + Truffle has is very hard (yet possible) to write, but would make it much easier for developers to write higher performance code. The JIT compilation would make things run faster (usually). The de-optimizer would mean that if a developer needs performance they can make it less dynamic and would automatically get the higher performance that this flag would offer without having to figure out where and when to turn on this flag.

When it comes to Ruby I will always be in favor of a more complicated and harder to write runtime instead of making things harder on developers. Sorry runtime devs. ;-)

Updated by mame (Yusuke Endoh) over 3 years ago

  • Status changed from Feedback to Rejected

I temporarily withdraw my proposal. I don't think my proposal is so bad, but I'll add quantitative information when I reopen. Thank you all for your comments.

Also available in: Atom PDF