summaryrefslogtreecommitdiff
path: root/prism
diff options
context:
space:
mode:
authorEarlopain <14981592+Earlopain@users.noreply.github.com>2025-12-23 14:41:09 +0100
committergit <svn-admin@ruby-lang.org>2025-12-29 14:14:00 +0000
commit65634d8df57ea1636efffb5f040fa31c156d307f (patch)
tree5612711e853a946f923a55b33b274f955bee66f8 /prism
parent14fbcf0e6ed37c4a0d15fd3f016778465f774f2c (diff)
[ruby/prism] Optimize ruby visitor
`compact_child_nodes` allocates an array. We can skip that step by simply yielding the nodes. Benchmark for visiting the rails codebase: ```rb require "prism" require "benchmark/ips" files = Dir.glob("../rails/**/*.rb") results = files.map { Prism.parse_file(it) } visitor = Prism::Visitor.new Benchmark.ips do |x| x.config(warmup: 3, time: 10) x.report do results.each do visitor.visit(it.value) end end end RubyVM::YJIT.enable Benchmark.ips do |x| x.config(warmup: 3, time: 10) x.report do results.each do visitor.visit(it.value) end end end ``` Before: ``` ruby 3.4.8 (2025-12-17 revision https://github.com/ruby/prism/commit/995b59f666) +PRISM [x86_64-linux] Warming up -------------------------------------- 1.000 i/100ms Calculating ------------------------------------- 2.691 (± 0.0%) i/s (371.55 ms/i) - 27.000 in 10.089422s ruby 3.4.8 (2025-12-17 revision https://github.com/ruby/prism/commit/995b59f666) +YJIT +PRISM [x86_64-linux] Warming up -------------------------------------- 1.000 i/100ms Calculating ------------------------------------- 7.278 (±13.7%) i/s (137.39 ms/i) - 70.000 in 10.071568s ``` After: ``` ruby 3.4.8 (2025-12-17 revision https://github.com/ruby/prism/commit/995b59f666) +PRISM [x86_64-linux] Warming up -------------------------------------- 1.000 i/100ms Calculating ------------------------------------- 3.429 (± 0.0%) i/s (291.65 ms/i) - 35.000 in 10.208580s ruby 3.4.8 (2025-12-17 revision https://github.com/ruby/prism/commit/995b59f666) +YJIT +PRISM [x86_64-linux] Warming up -------------------------------------- 1.000 i/100ms Calculating ------------------------------------- 16.815 (± 0.0%) i/s (59.47 ms/i) - 169.000 in 10.054668s ``` ~21% faster on the interpreter, ~56% with YJIT https://github.com/ruby/prism/commit/bf631750cf
Diffstat (limited to 'prism')
-rw-r--r--prism/templates/lib/prism/compiler.rb.erb4
-rw-r--r--prism/templates/lib/prism/node.rb.erb25
-rw-r--r--prism/templates/lib/prism/visitor.rb.erb4
3 files changed, 28 insertions, 5 deletions
diff --git a/prism/templates/lib/prism/compiler.rb.erb b/prism/templates/lib/prism/compiler.rb.erb
index 9102025c20..66dbe666b9 100644
--- a/prism/templates/lib/prism/compiler.rb.erb
+++ b/prism/templates/lib/prism/compiler.rb.erb
@@ -29,14 +29,14 @@ module Prism
# Visit the child nodes of the given node.
def visit_child_nodes(node)
- node.compact_child_nodes.map { |node| node.accept(self) }
+ node.each_child_node.map { |node| node.accept(self) }
end
<%- nodes.each_with_index do |node, index| -%>
<%= "\n" if index != 0 -%>
# Compile a <%= node.name %> node
def visit_<%= node.human %>(node)
- node.compact_child_nodes.map { |node| node.accept(self) }
+ node.each_child_node.map { |node| node.accept(self) }
end
<%- end -%>
end
diff --git a/prism/templates/lib/prism/node.rb.erb b/prism/templates/lib/prism/node.rb.erb
index ceee2b0ffe..c97c029d3b 100644
--- a/prism/templates/lib/prism/node.rb.erb
+++ b/prism/templates/lib/prism/node.rb.erb
@@ -187,7 +187,7 @@ module Prism
while (node = queue.shift)
result << node
- node.compact_child_nodes.each do |child_node|
+ node.each_child_node do |child_node|
child_location = child_node.location
start_line = child_location.start_line
@@ -259,6 +259,13 @@ module Prism
alias deconstruct child_nodes
+ # With a block given, yields each child node. Without a block, returns
+ # an enumerator that contains each child node. Excludes any `nil`s in
+ # the place of optional nodes that were not present.
+ def each_child_node
+ raise NoMethodError, "undefined method `each_child_node' for #{inspect}"
+ end
+
# Returns an array of child nodes, excluding any `nil`s in the place of
# optional nodes that were not present.
def compact_child_nodes
@@ -335,6 +342,22 @@ module Prism
}.compact.join(", ") %>]
end
+ # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node]
+ def each_child_node
+ return to_enum(:each_child_node) unless block_given?
+
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ yield <%= field.name %>
+ <%- when Prism::Template::OptionalNodeField -%>
+ yield <%= field.name %> if <%= field.name %>
+ <%- when Prism::Template::NodeListField -%>
+ <%= field.name %>.each {|node| yield node }
+ <%- end -%>
+ <%- end -%>
+ end
+
# def compact_child_nodes: () -> Array[Node]
def compact_child_nodes
<%- if node.fields.any? { |field| field.is_a?(Prism::Template::OptionalNodeField) } -%>
diff --git a/prism/templates/lib/prism/visitor.rb.erb b/prism/templates/lib/prism/visitor.rb.erb
index b1a03c3f1a..76f907724f 100644
--- a/prism/templates/lib/prism/visitor.rb.erb
+++ b/prism/templates/lib/prism/visitor.rb.erb
@@ -20,7 +20,7 @@ module Prism
# Visits the child nodes of `node` by calling `accept` on each one.
def visit_child_nodes(node)
# @type self: _Visitor
- node.compact_child_nodes.each { |node| node.accept(self) }
+ node.each_child_node { |node| node.accept(self) }
end
end
@@ -48,7 +48,7 @@ module Prism
<%= "\n" if index != 0 -%>
# Visit a <%= node.name %> node
def visit_<%= node.human %>(node)
- node.compact_child_nodes.each { |node| node.accept(self) }
+ node.each_child_node { |node| node.accept(self) }
end
<%- end -%>
end