Issue #8110 has been updated by naruse (Yui NARUSE).


headius (Charles Nutter) wrote:
> naruse (Yui NARUSE) wrote:
> > You may misunderstand, unlike Perl, Ruby's setting global variable cost is small.
> > Ruby only set a MatchData object to its scope.
> 
> That ignores the fact that without $~, the scope wouldn't need to be allocated either. In JRuby, when we know there's no $~ use, we allocate no scope; JVM can then inline our methods and avoid all allocation, putting locals in registers and speeding things up tremendously.

On such case, Regexp::SKIP_GLOBALS is useless.
Without it JRuby would optimize to skip globals.

> As an example of how much it helps... MRI 2.0.0 was changed to not allocate frames for core class methods, a change we made a couple years ago for JRuby 1.6. This had a massive impact on performance. If MRI could do this for Ruby methods as well, it would improve things further, but $~ and its implicit nature prevent that from being feasible right now.

If so, ko1 should implement some way to treat $~ without frames.

> > $~ (Regexp.last_match) gets it.
> > The implementation of $& (Regexp.last_match[0]), $` (Regexp.last_match.pre_match), and $' (Regexp.last_match.post_match)
> > are get $~ and call [0], pre_match, or post_match.
> > So setting cost is very small (0.2 second for 1,000,000 times).
> 
> The scope cost is the hidden cost.

This doesn't include the cost to create a new scope because they are the same scope on $~ context.

> > And if it doesn't set global variable, it means that it can't recycle previous MatchData object.
> > So it allocates new MatchData object each time, it costs both allocation and GC.
> 
> There are other ways to reduce the cost of allocating MatchData. In the end the MatchData object isn't as big as the matcher structures from the regexp engine anyway, right?

My String#include?(regexp) patch in Feature #8206 is an example.

> > On following case, its cost is beyond the setting cost.
> >   r = Regexp.new(foo, Regexp::SKIP_GLOBALS); 1000000.times{r=~"foo"}
> 
> The cost here is as much the closure binding as it is the setting of $~. If =~ did not set $~, no binding at all would be required for the closure and it would boil down to just the cost of calling =~ and creating the literal string.

block doesn't make scope for $~.
And the time I show is compared between original and simply commented out rb_backref_set version.

> > Therefore if you want speed up, you must remove making MatchData object.
> > String#match won't speed up so much because its API need creating MatchData object.
> > (moreover its current implementation uses $~)
> 
> String#match would be known to not need $~, and implementations could avoid allocating the memory used to store $~ (not the MatchData but the method scope).

Setting $~ itself doesn't cause memory allocation because it is only setting the same object to VM.

> I will grant that since MRI does not have a JIT compiler, you need artificial scopes/frames anyway, but for implementations with optimizing JITs (JRuby, Rubinius) $~ is one of the biggest barriers to optimization.

I know $~ prevents optimization because I used Perl before.
But it doesn't relate to Regexp::SKIP_GLOBALS because $~ is still here even if you usually use Regexp::SKIP_GLOBALS.
----------------------------------------
Feature #8110: Regex methods not changing global variables
https://bugs.ruby-lang.org/issues/8110#change-38691

Author: prijutme4ty (Ilya Vorontsov)
Status: Assigned
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: core
Target version: next minor


It is useful to have methods allowing pattern matching without setting global variables. It can be very hard to understand where the problem is when you for example insert a string like `puts pat === my_str` and your program fails in a place which is far-far away from inserted place. This can happen due to replacing global variables of previous pattern match. I caught to this when placed pattern-match inside case-statement and shadowed global vars which were initially filled by match in when-statement.
For now one can extract pattern matching into another method thus defining method-scope for that variables. But sometimes it looks like an overkill. May be simple method like #match_globalsafe can prevent that kind of errors. At least when a programmer see such a method in a list of methods, he's warned that usual match can cause such problems.


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