Conversation
This avoids allocating a new String each time and may assist the
JIT or CPU branch optimization. Results on 17 show a reasonable
improvement:
Script
```
10.times do
t = Time.now
i = 0; while i < 100_000_000
case RUBY_ENGINE
when "foo"
when "bar"
when "baz"
when "quux"
when "jruby"
end
i += 1
end
puts Time.now - t
end
```
Before:
```
$ jruby -Xcompile.invokedynamic blah.rb
5.6268959999999995
5.126104
5.089117000000001
5.156553
5.1014420000000005
5.1802779999999995
5.112481
5.135682
5.108746
5.164584
```
After:
```
$ jruby -Xcompile.invokedynamic blah.rb
3.3244930000000004
3.0746480000000003
2.930905
3.020159
2.925828
2.925954
2.926557
2.9447579999999998
2.926937
2.925714
```
This difference is reduced but not eliminated on a JIT with better
escape analysis (the overall reduction is due to the removal of the
loop fixnums):
Before:
```
$ JAVA_HOME=~/graalvm-ce-java17-22.3.0/Contents/Home jruby -Xcompile.invokedynamic -Xfixnum.cache=false blah.rb
4.947942
4.683816
3.772222
3.8641159999999997
3.782633
3.7981920000000002
3.816897
3.786005
3.784264
3.784209
```
After:
```
$ JAVA_HOME=~/graalvm-ce-java17-22.3.0/Contents/Home jruby -Xcompile.invokedynamic -Xfixnum.cache=false blah.rb
3.1830589999999996
2.999025
2.135789
2.051477
1.9829130000000001
2.211411
2.1756149999999996
2.177314
2.177531
2.180635
```
This breaks compatibility with CRuby but in a way that should not
typically be visible to any user. Both `case "foo"` and
`when "foo"` will now use a cached frozen string. The `case` form
is unusual but has a more visible behavior change due to the
`when` elements receiving a now-frozen string via `===`i, but
expecting the `case` value to be mutable here seems unreasonable.
The `when` form will now call `===` against frozen strings, only
visible if `String#===` has been patched in some way.
Like the fixnum optimization, this directly embeds the ID values
of literal symbol whens and emits a fast JVM switch (usually will
be a lookupswitch since symbol IDs are quite variable). The
incoming value is typechecked against RubySymbol and if it
matches, getID is called and used as the input for a switch.
This improves performance of a matching case/when by roughly 5x
on a VM that eliminates the benchmark loop:
Script:
```ruby
100.times do
t = Time.now
i = 0; while i < 100_000_000
case RUBY_ENGINE
when :"foo"
when :"bar"
when :"baz"
when :"quux"
when :"jruby"
end
i += 1
end
puts Time.now - t
end
```
Before:
```
$ JAVA_HOME=~/graalvm-ce-java17-22.3.0/Contents/Home jruby -Xcompile.invokedynamic -Xfixnum.cache=false blah.rb
0.840298
0.458168
0.41852999999999996
0.40532500000000005
0.402366
0.40269200000000005
0.40293
0.406479
0.40441400000000005
0.40388100000000005
0.403687
0.402861
0.439501
...
```
After:
```
$ JAVA_HOME=~/graalvm-ce-java17-22.3.0/Contents/Home jruby -Xcompile.invokedynamic -Xfixnum.cache=false blah.rb
0.125933
0.081487
0.08532999999999999
0.111028
0.085825
0.08541699999999999
0.08525300000000001
0.08525300000000001
0.08675699999999999
0.08553300000000001
0.08535699999999999
0.08566299999999999
0.085715
...
```
This may not match semantics of CRuby if `Symbol#===` is patched
but we can add detection for that case if necessary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Just a gaggle of small optimizations to case/when.