From 4183befdd35096fb2dd7e56bc000093ebd6f2461 Mon Sep 17 00:00:00 2001 From: NAITOH Jun Date: Sun, 17 May 2026 16:41:44 +0900 Subject: [PATCH] Optimization of REXML::Parent#index ## Benchmark ``` $ benchmark-driver benchmark/xpath.yaml before after before(YJIT) after(YJIT) REXML::XPath.match(REXML::Document.new(xml), 'a//a') 4.016k 4.274k 3.942k 4.106k i/s - 100.000 times in 0.024899s 0.023398s 0.025371s 0.024357s REXML::XPath.match(REXML::Document.new(xml), '//a//a') 1.327k 1.435k 1.659k 1.742k i/s - 100.000 times in 0.075383s 0.069665s 0.060272s 0.057402s REXML::Document.new(xml_wide).root.children.first.next_sibling_node 5.883M 7.144M 219.780k 279.330k i/s - 100.000 times in 0.000017s 0.000014s 0.000455s 0.000358s REXML::Document.new(xml_wide).root.children.last.previous_sibling_node 109.051k 207.900k 97.466k 139.276k i/s - 100.000 times in 0.000917s 0.000481s 0.001026s 0.000718s Comparison: REXML::XPath.match(REXML::Document.new(xml), 'a//a') after: 4273.9 i/s after(YJIT): 4105.6 i/s - 1.04x slower before: 4016.2 i/s - 1.06x slower before(YJIT): 3941.5 i/s - 1.08x slower REXML::XPath.match(REXML::Document.new(xml), '//a//a') after(YJIT): 1742.1 i/s before(YJIT): 1659.1 i/s - 1.05x slower after: 1435.4 i/s - 1.21x slower before: 1326.6 i/s - 1.31x slower REXML::Document.new(xml_wide).root.children.first.next_sibling_node after: 7143515.6 i/s before: 5882549.8 i/s - 1.21x slower after(YJIT): 279330.0 i/s - 25.57x slower before(YJIT): 219780.0 i/s - 32.50x slower REXML::Document.new(xml_wide).root.children.last.previous_sibling_node after: 207900.1 i/s after(YJIT): 139275.9 i/s - 1.49x slower before: 109051.2 i/s - 1.91x slower before(YJIT): 97465.8 i/s - 2.13x slower ``` - YJIT=ON : 1.04x - 1.42x faster - YJIT=OFF : 1.06x - 1.91x faster --- benchmark/xpath.yaml | 8 ++++++++ lib/rexml/parent.rb | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/benchmark/xpath.yaml b/benchmark/xpath.yaml index 072e3160..b5cc7739 100644 --- a/benchmark/xpath.yaml +++ b/benchmark/xpath.yaml @@ -28,6 +28,14 @@ prelude: | xml = '' * DEPTH + '' * DEPTH doc = REXML::Document.new(xml) + WIDTH = 200 + xml_wide = '' + '' * WIDTH + '' + doc_wide = REXML::Document.new(xml_wide) + first_child = doc_wide.root.children.first + last_child = doc_wide.root.children.last + benchmark: "REXML::XPath.match(REXML::Document.new(xml), 'a//a')" : REXML::XPath.match(doc, "a//a") "REXML::XPath.match(REXML::Document.new(xml), '//a//a')" : REXML::XPath.match(doc, "//a//a") + "REXML::Document.new(xml_wide).root.children.first.next_sibling_node" : first_child.next_sibling_node + "REXML::Document.new(xml_wide).root.children.last.previous_sibling_node" : last_child.previous_sibling_node diff --git a/lib/rexml/parent.rb b/lib/rexml/parent.rb index 6a53b37a..90004c6b 100644 --- a/lib/rexml/parent.rb +++ b/lib/rexml/parent.rb @@ -121,9 +121,7 @@ def to_a # @return the index of the child, or nil if the object is not a child # of this parent. def index( child ) - count = -1 - @children.find { |i| count += 1 ; i.hash == child.hash } - count + @children.index { |c| c.equal?(child) } end # @return the number of children of this parent