Skip to content

Commit 16adb93

Browse files
Earlopainmatzbot
authored andcommitted
[ruby/prism] Optimize ripper translator
Creating state classes is pretty expensive. Since they are not modifiable, we can reuse them instead. Benchmark script: ```rb require "ripper" require "prism" require "benchmark/ips" codes = Dir["**/*.rb"].map { File.read(it) } Benchmark.ips do |x| x.config(time: 10) 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 ruby/prism@553f1675f3) +PRISM [x86_64-linux] Warming up -------------------------------------- prism 1.000 i/100ms ripper 1.000 i/100ms Calculating ------------------------------------- prism 0.293 (± 0.0%) i/s (3.42 s/i) - 3.000 in 10.248348s ripper 0.633 (± 0.0%) i/s (1.58 s/i) - 7.000 in 11.055687s Comparison: ripper: 0.6 i/s prism: 0.3 i/s - 2.16x slower ``` After ``` ruby 4.0.0 (2025-12-25 revision ruby/prism@553f1675f3) +PRISM [x86_64-linux] Warming up -------------------------------------- prism 1.000 i/100ms ripper 1.000 i/100ms Calculating ------------------------------------- prism 0.486 (± 0.0%) i/s (2.06 s/i) - 5.000 in 10.280413s ripper 0.635 (± 0.0%) i/s (1.58 s/i) - 7.000 in 11.027169s Comparison: ripper: 0.6 i/s prism: 0.5 i/s - 1.31x slower ``` ruby/prism@bdde16554c
1 parent c939330 commit 16adb93

File tree

2 files changed

+10
-3
lines changed

2 files changed

+10
-3
lines changed

lib/prism/lex_compat.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ def result
667667

668668
event = RIPPER.fetch(token.type)
669669
value = token.value
670-
lex_state = Translation::Ripper::Lexer::State.new(lex_state)
670+
lex_state = Translation::Ripper::Lexer::State.cached(lex_state)
671671

672672
token =
673673
case event
@@ -734,7 +734,7 @@ def result
734734
counter += { on_embexpr_beg: -1, on_embexpr_end: 1 }[current_event] || 0
735735
end
736736

737-
Translation::Ripper::Lexer::State.new(result_value[current_index][1])
737+
Translation::Ripper::Lexer::State.cached(result_value[current_index][1])
738738
else
739739
previous_state
740740
end

lib/prism/translation/ripper/lexer.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ def |(i) self.class.new(to_int | i) end
3838
def allbits?(i) to_int.allbits?(i) end
3939
def anybits?(i) to_int.anybits?(i) end
4040
def nobits?(i) to_int.nobits?(i) end
41+
42+
# Instances are frozen and there are only a handful of them so we cache them here.
43+
STATES = Hash.new { |h,k| h[k] = State.new(k) }
44+
45+
def self.cached(i)
46+
STATES[i]
47+
end
4148
end
4249

4350
class Elem
@@ -47,7 +54,7 @@ def initialize(pos, event, tok, state, message = nil)
4754
@pos = pos
4855
@event = event
4956
@tok = tok
50-
@state = State.new(state)
57+
@state = State.cached(state)
5158
@message = message
5259
end
5360

0 commit comments

Comments
 (0)