Rewrite Array#each in Ruby using Primitive#9533
Conversation
dca75f2 to
35f3b4e
Compare
|
Nice. Happy to see this working. Surprised it's so far in YJIT as well! Ideally, for YJIT, we'd like to be able to avoid doing a function call at all, so we can generate tight inlined code though, so we might need a specialized instruction instead of a cexpr? What do you think? |
I think it's possible without a new instruction. |
3c97813 to
bede16a
Compare
bede16a to
3aaa6de
Compare
0d58851 to
4f30c21
Compare
4f30c21 to
6b5e44b
Compare
2e2cd3f to
d98ac2c
Compare
In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
) In order to provide compatibility with TruffleRuby that implements a lot of its core library methods in Ruby, and for future versions of CRuby that is increasingly doing the same, we need to be able to filter all backtrace locations where the `path` starts with `<internal:`. [This gist](https://gist.github.com/eregon/912e6359e83781c5fa1c638d3768c526) shows the current state of the methods implemented in Ruby in CRuby, JRuby and TruffleRuby. Most recently [CRuby started implementing `Array#each` in Ruby](ruby/ruby#9533), making it usages of `each` visible in backtraces with an `<internal:array>` path. This means that in order to be compatible with CRuby 3.4, Sorbet runtime needs to start filtering out backtrace locations that start with `<internal:`. By encapsulating the caller location search logic into a singleton method, we can apply that filtering in a single location and avoid having to repeat it in multiple places.
Inspired by https://bugs.ruby-lang.org/issues/20182 and ruby#9533. This PR provides a performance boost to Array#find when run using JIT compilation. This is achieved by implementing Array#find in Ruby, which the JIT compiler can optimise. [PR#15189](ruby#15189) added a C implementation for Array#find instead of relying on Enumerable#find. This PR extends this by adding a Ruby implementation. I used the so_fasta benchmark to measure performance. No change in interpreted performance before/after: $ benchmark-driver -e "~/.rubies/ruby-master/bin/ruby; ~/.rubies/ruby-array-find-native/bin/ruby" ../benchmark/so_fasta.rb Calculating ------------------------------------- ~/.rubies/ruby-master/bin/ruby ~/.rubies/ruby-array-find-native/bin/ruby so_fasta 0.393 0.393 i/s - 1.000 times in 2.543209s 2.545514s Comparison: so_fasta ~/.rubies/ruby-master/bin/ruby: 0.4 i/s ~/.rubies/ruby-array-find-native/bin/ruby: 0.4 i/s - 1.00x slower With YJIT enabled the speed is almost twice as fast: $ benchmark-driver -e "~/.rubies/ruby-array-find-native/bin/ruby; ~/.rubies/ruby-array-find-native/bin/ruby --yjit" ../benchmark/so_fasta.rb Calculating ------------------------------------- ~/.rubies/ruby-array-find-native/bin/ruby ~/.rubies/ruby-array-find-native/bin/ruby --yjit so_fasta 0.393 0.770 i/s - 1.000 times in 2.547550s 1.298371s Comparison: so_fasta ~/.rubies/ruby-array-find-native/bin/ruby --yjit: 0.8 i/s ~/.rubies/ruby-array-find-native/bin/ruby: 0.4 i/s - 1.96x slower
Inspired by https://bugs.ruby-lang.org/issues/20182 and ruby#9533. This PR provides a performance boost to Array#find when run using JIT compilation. This is achieved by implementing Array#find in Ruby, which the JIT compiler can optimise. [PR#15189](ruby#15189) added a C implementation for Array#find instead of relying on Enumerable#find. This PR extends this by adding a Ruby implementation. I used the so_fasta benchmark to measure performance. No change in interpreted performance before/after: $ benchmark-driver -e "~/.rubies/ruby-master/bin/ruby; ~/.rubies/ruby-array-find-native/bin/ruby" ../benchmark/so_fasta.rb Calculating ------------------------------------- ~/.rubies/ruby-master/bin/ruby ~/.rubies/ruby-array-find-native/bin/ruby so_fasta 0.393 0.393 i/s - 1.000 times in 2.543209s 2.545514s Comparison: so_fasta ~/.rubies/ruby-master/bin/ruby: 0.4 i/s ~/.rubies/ruby-array-find-native/bin/ruby: 0.4 i/s - 1.00x slower With YJIT enabled Array#find is almost twice as fast: $ benchmark-driver -e "~/.rubies/ruby-array-find-native/bin/ruby; ~/.rubies/ruby-array-find-native/bin/ruby --yjit" ../benchmark/so_fasta.rb Calculating ------------------------------------- ~/.rubies/ruby-array-find-native/bin/ruby ~/.rubies/ruby-array-find-native/bin/ruby --yjit so_fasta 0.393 0.770 i/s - 1.000 times in 2.547550s 1.298371s Comparison: so_fasta ~/.rubies/ruby-array-find-native/bin/ruby --yjit: 0.8 i/s ~/.rubies/ruby-array-find-native/bin/ruby: 0.4 i/s - 1.96x slower
Inspired by https://bugs.ruby-lang.org/issues/20182 and ruby#9533. This PR provides a performance boost to Array#find when run using JIT compilation. This is achieved by implementing Array#find in Ruby, which the JIT compiler can optimise. [PR#15189](ruby#15189) added a C implementation for Array#find instead of relying on Enumerable#find. This PR extends this by adding a Ruby implementation. I used the so_fasta benchmark to measure performance. No change in interpreted performance before/after: $ benchmark-driver -e "~/.rubies/ruby-master/bin/ruby; ~/.rubies/ruby-array-find-native/bin/ruby" ../benchmark/so_fasta.rb Calculating ------------------------------------- ~/.rubies/ruby-master/bin/ruby ~/.rubies/ruby-array-find-native/bin/ruby so_fasta 0.393 0.393 i/s - 1.000 times in 2.543209s 2.545514s Comparison: so_fasta ~/.rubies/ruby-master/bin/ruby: 0.4 i/s ~/.rubies/ruby-array-find-native/bin/ruby: 0.4 i/s - 1.00x slower With YJIT enabled Array#find is almost twice as fast: $ benchmark-driver -e "~/.rubies/ruby-array-find-native/bin/ruby; ~/.rubies/ruby-array-find-native/bin/ruby --yjit" ../benchmark/so_fasta.rb Calculating ------------------------------------- ~/.rubies/ruby-array-find-native/bin/ruby ~/.rubies/ruby-array-find-native/bin/ruby --yjit so_fasta 0.393 0.770 i/s - 1.000 times in 2.547550s 1.298371s Comparison: so_fasta ~/.rubies/ruby-array-find-native/bin/ruby --yjit: 0.8 i/s ~/.rubies/ruby-array-find-native/bin/ruby: 0.4 i/s - 1.96x slower
Inspired by https://bugs.ruby-lang.org/issues/20182 and #9533. This PR provides a performance boost to Array#find when run using JIT compilation. This is achieved by implementing Array#find in Ruby, which the JIT compiler can optimise. [PR#15189](#15189) added a C implementation for Array#find instead of relying on Enumerable#find. This PR extends this by adding a Ruby implementation. I used the so_fasta benchmark to measure performance. No change in interpreted performance before/after: $ benchmark-driver -e "~/.rubies/ruby-master/bin/ruby; ~/.rubies/ruby-array-find-native/bin/ruby" ../benchmark/so_fasta.rb Calculating ------------------------------------- ~/.rubies/ruby-master/bin/ruby ~/.rubies/ruby-array-find-native/bin/ruby so_fasta 0.393 0.393 i/s - 1.000 times in 2.543209s 2.545514s Comparison: so_fasta ~/.rubies/ruby-master/bin/ruby: 0.4 i/s ~/.rubies/ruby-array-find-native/bin/ruby: 0.4 i/s - 1.00x slower With YJIT enabled Array#find is almost twice as fast: $ benchmark-driver -e "~/.rubies/ruby-array-find-native/bin/ruby; ~/.rubies/ruby-array-find-native/bin/ruby --yjit" ../benchmark/so_fasta.rb Calculating ------------------------------------- ~/.rubies/ruby-array-find-native/bin/ruby ~/.rubies/ruby-array-find-native/bin/ruby --yjit so_fasta 0.393 0.770 i/s - 1.000 times in 2.547550s 1.298371s Comparison: so_fasta ~/.rubies/ruby-array-find-native/bin/ruby --yjit: 0.8 i/s ~/.rubies/ruby-array-find-native/bin/ruby: 0.4 i/s - 1.96x slower
Same as #6687, but race condition-free.
microbenchmark
Interpreter
Thanks to
Primitive, the interpreter doesn't slow down.YJIT
Even faster than #6687.
yjit-bench
See #9622.