Skip to content

Commit 69e3928

Browse files
committed
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 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 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`.
1 parent 41c7c12 commit 69e3928

1 file changed

Lines changed: 16 additions & 8 deletions

File tree

lib/prism/lex_compat.rb

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# frozen_string_literal: true
22
# :markup: markdown
33

4-
require "delegate"
5-
64
module Prism
75
# This class is responsible for lexing the source using prism and then
86
# converting those tokens to be compatible with Ripper. In the vast majority
@@ -201,27 +199,37 @@ def deconstruct_keys(keys)
201199
# When we produce tokens, we produce the same arrays that Ripper does.
202200
# However, we add a couple of convenience methods onto them to make them a
203201
# little easier to work with. We delegate all other methods to the array.
204-
class Token < SimpleDelegator
205-
# @dynamic initialize, each, []
202+
class Token
203+
def initialize(data)
204+
@data = data
205+
end
206206

207207
# The location of the token in the source.
208208
def location
209-
self[0]
209+
@data[0]
210210
end
211211

212212
# The type of the token.
213213
def event
214-
self[1]
214+
@data[1]
215215
end
216216

217217
# The slice of the source that this token represents.
218218
def value
219-
self[2]
219+
@data[2]
220220
end
221221

222222
# The state of the lexer when this token was produced.
223223
def state
224-
self[3]
224+
@data[3]
225+
end
226+
227+
def respond_to_missing?(name, include_private = false)
228+
@data.respond_to?(name, include_private)
229+
end
230+
231+
def method_missing(name, ...)
232+
@data.send(name, ...)
225233
end
226234
end
227235

0 commit comments

Comments
 (0)