diff options
| author | Earlopain <14981592+Earlopain@users.noreply.github.com> | 2026-01-24 20:34:23 +0100 |
|---|---|---|
| committer | git <svn-admin@ruby-lang.org> | 2026-01-24 23:10:34 +0000 |
| commit | f7bc28d8244cd69b78382d193421abda1204aa73 (patch) | |
| tree | 293df0eab0433f65340e3a2f2da8b00413f1b3d3 /lib | |
| parent | 1de6133825862ef987160a5ba39f5af685610f4e (diff) | |
[ruby/prism] Further optimize ripper translator by not using `delegate`
Using it seems pretty bad for performance:
```rb
require "benchmark/ips"
require "prism"
require "ripper"
codes = Dir["**/*.rb"].map { File.read(it) }
Benchmark.ips do |x|
x.report("prism") { codes.each { Prism::Translation::Ripper.lex(it) } }
x.report("ripper") { codes.each { Ripper.lex(it) } }
x.compare!
end
```
```
# Before
ruby 4.0.0 (2025-12-25 revision https://github.com/ruby/prism/commit/553f1675f3) +PRISM [x86_64-linux]
Warming up --------------------------------------
prism 1.000 i/100ms
ripper 1.000 i/100ms
Calculating -------------------------------------
prism 0.319 (± 0.0%) i/s (3.14 s/i) - 2.000 in 6.276154s
ripper 0.647 (± 0.0%) i/s (1.54 s/i) - 4.000 in 6.182662s
Comparison:
ripper: 0.6 i/s
prism: 0.3 i/s - 2.03x slower
# After
ruby 4.0.0 (2025-12-25 revision https://github.com/ruby/prism/commit/553f1675f3) +PRISM [x86_64-linux]
Warming up --------------------------------------
prism 1.000 i/100ms
ripper 1.000 i/100ms
Calculating -------------------------------------
prism 0.482 (± 0.0%) i/s (2.08 s/i) - 3.000 in 6.225603s
ripper 0.645 (± 0.0%) i/s (1.55 s/i) - 4.000 in 6.205636s
Comparison:
ripper: 0.6 i/s
prism: 0.5 i/s - 1.34x slower
```
`vernier` tells me it does `method_missing` even for explicitly defined methods like `location`.
https://github.com/ruby/prism/commit/2ea81398cc
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/prism/lex_compat.rb | 30 |
1 files changed, 22 insertions, 8 deletions
diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb index 597e63c73e..c14a9f1603 100644 --- a/lib/prism/lex_compat.rb +++ b/lib/prism/lex_compat.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # :markup: markdown -require "delegate" - module Prism # This class is responsible for lexing the source using prism and then # converting those tokens to be compatible with Ripper. In the vast majority @@ -201,27 +199,43 @@ module Prism # When we produce tokens, we produce the same arrays that Ripper does. # However, we add a couple of convenience methods onto them to make them a # little easier to work with. We delegate all other methods to the array. - class Token < SimpleDelegator - # @dynamic initialize, each, [] + class Token < BasicObject + # Create a new token object with the given ripper-compatible array. + def initialize(array) + @array = array + end # The location of the token in the source. def location - self[0] + @array[0] end # The type of the token. def event - self[1] + @array[1] end # The slice of the source that this token represents. def value - self[2] + @array[2] end # The state of the lexer when this token was produced. def state - self[3] + @array[3] + end + + # We want to pretend that this is just an Array. + def ==(other) # :nodoc: + @array == other + end + + def respond_to_missing?(name, include_private = false) # :nodoc: + @array.respond_to?(name, include_private) + end + + def method_missing(name, ...) # :nodoc: + @array.send(name, ...) end end |
