Skip to content

Commit

Permalink
Optimize implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
makenowjust committed May 30, 2024
1 parent 6e1f7a9 commit ab4962c
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 13 deletions.
10 changes: 6 additions & 4 deletions lib/rexml/css_selector/adapters/rexml_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ def get_attribute(element, name, namespace = nil, attribute_name_case = :sensiti

case attribute_name_case
in :sensitive
element.attribute(name, namespace)&.value
attrs = element.attributes
# NOTE: `REXML::Element.attribute` is too slow to use.
# Therefore, we call `REXML::Attributes#[]` instead.
namespace.nil? ? attrs[name] : attrs.get_attribute_ns(namespace, name)&.value
in :insensitive
name = name.downcase(:ascii)
target_attr = nil
Expand Down Expand Up @@ -65,9 +68,8 @@ def each_child_element(element, &)
element.each_child { yield _1 if element?(_1) }
end

def each_recursive_element(element, &)
element.each_recursive(&)
end
# NOTE: `REXML::Element#each_recursive` is too slow.
# Therefore, we use our default implementation instead.

# INSTANCE is the default instance.
INSTANCE = new
Expand Down
8 changes: 6 additions & 2 deletions lib/rexml/css_selector/base_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,13 @@ def get_id(element)

# Enumerates the elements in +element+
def each_recursive_element(element, &)
each_child_element(element) do |child|
stack = []
each_child_element(element) { stack.unshift _1 }
until stack.empty?
child = stack.pop
yield child
each_recursive_element(child, &)
n = stack.size
each_child_element(child) { stack.insert n, _1 }
end
end
end
Expand Down
18 changes: 11 additions & 7 deletions tool/bench.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,41 @@

filepath = Fixture.filepath("sizzle.html")
selector = "h2, #qunit-fixture p"
bench_rexml_xpath = false
n = 1000

opt = OptionParser.new

opt.on("-f <filepath>") { filepath = _1 }
opt.on("-s <selector>") { selector = _1 }
opt.on("-n <n>") { n = _1.to_i }
opt.on("-o <filepath>") { _1 }
opt.on("--bench-rexml-xpath") { bench_rexml_xpath = true }

puts "==> Parse command-line options"
opt.parse!(ARGV)

puts <<~HERE
filepath: #{filepath.inspect}
selector: #{selector.inspect}
n: #{n}
filepath: #{filepath.inspect}
selector: #{selector.inspect}
n: #{n}
bench_rexml_xpath: #{bench_rexml_xpath}
HERE

puts "==> Load and parse a XML file"

content = File.read(filepath)
nokogiri_doc = Nokogiri.HTML(content)
selector_xpath = Nokogiri::CSS.xpath_for(selector).join(" | ")
rexml_doc = REXML::Document.new(content)

puts " XPath: #{selector_xpath}"
if bench_rexml_xpath
selector_xpath = Nokogiri::CSS.xpath_for(selector).join(" | ")
puts " XPath: #{selector_xpath}"
end

puts "==> Start a benchmark"

Benchmark.bm do |x|
x.report("Nokogiri ") { n.times { nokogiri_doc.css(selector) } }
x.report("REXML (XPath) ") { n.times { rexml_doc.get_elements(selector_xpath) } }
x.report("REXML (XPath) ") { n.times { rexml_doc.get_elements(selector_xpath) } } if bench_rexml_xpath
x.report("REXML::CSSSelector") { n.times { REXML::CSSSelector.select_all(rexml_doc, selector) } }
end

0 comments on commit ab4962c

Please # to comment.