From 418bc94b295716f9ee88d2df02d14627dfd09937 Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Thu, 13 Jun 2024 14:55:32 +0900 Subject: [PATCH 1/3] Improve `Element#attribute` as 6500x faster `Element#namespaces` is heavy method because this method needs to traverse all ancestors of the element. `Element#attribute` calls `namespaces` redundantly, so it is much slower. This commit reduces `namespaces` calls in `Element#attribute`. Also, this commit removes a redundant `respond_to?` because `namespaces` must return `Hash` in the current implementation. --- benchmark/attribute.yaml | 43 ++++++++++++++++++++++++++++++++++++++++ lib/rexml/element.rb | 9 ++------- 2 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 benchmark/attribute.yaml diff --git a/benchmark/attribute.yaml b/benchmark/attribute.yaml new file mode 100644 index 00000000..b1325106 --- /dev/null +++ b/benchmark/attribute.yaml @@ -0,0 +1,43 @@ +loop_count: 1000 +contexts: + - gems: + rexml: 3.2.6 + require: false + prelude: require 'rexml' + - name: master + prelude: | + $LOAD_PATH.unshift(File.expand_path("lib")) + require 'rexml' + - name: 3.2.6(YJIT) + gems: + rexml: 3.2.6 + require: false + prelude: | + require 'rexml' + RubyVM::YJIT.enable + - name: master(YJIT) + prelude: | + $LOAD_PATH.unshift(File.expand_path("lib")) + require 'rexml' + RubyVM::YJIT.enable + +prelude: | + require 'rexml/document' + + xml_source = "" + 100.times do + xml_source = "#{xml_source}" + end + xml_source = "#{xml_source}" + + document = REXML::Document.new(xml_source) + deepest_node = document.root + until deepest_node.elements.size.zero? + deepest_node = deepest_node.elements.first + end + +benchmark: + attribute_with_ns: | + deepest_node.attribute("with_ns", "xyz") + attribute_without_ns: | + deepest_node.attribute("without_ns") diff --git a/lib/rexml/element.rb b/lib/rexml/element.rb index 2899759d..a5808d7c 100644 --- a/lib/rexml/element.rb +++ b/lib/rexml/element.rb @@ -1276,16 +1276,11 @@ def [](name_or_index) # document.root.attribute("x", "a") # => a:x='a:x' # def attribute( name, namespace=nil ) - prefix = nil - if namespaces.respond_to? :key - prefix = namespaces.key(namespace) if namespace - else - prefix = namespaces.index(namespace) if namespace - end + prefix = namespaces.key(namespace) if namespace prefix = nil if prefix == 'xmlns' ret_val = - attributes.get_attribute( "#{prefix ? prefix + ':' : ''}#{name}" ) + attributes.get_attribute( prefix ? "#{prefix}:#{name}" : name ) return ret_val unless ret_val.nil? return nil if prefix.nil? From 53c6d5fa85df7976bd559ccc5618686acaacbec9 Mon Sep 17 00:00:00 2001 From: Hiroya Fujinami Date: Thu, 13 Jun 2024 15:32:59 +0900 Subject: [PATCH 2/3] Update benchmark/attribute.yaml Co-authored-by: Sutou Kouhei --- benchmark/attribute.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/benchmark/attribute.yaml b/benchmark/attribute.yaml index b1325106..812329be 100644 --- a/benchmark/attribute.yaml +++ b/benchmark/attribute.yaml @@ -31,10 +31,7 @@ prelude: | xml_source = "#{xml_source}" document = REXML::Document.new(xml_source) - deepest_node = document.root - until deepest_node.elements.size.zero? - deepest_node = deepest_node.elements.first - end + deepest_node = document.elements["//deepest"] benchmark: attribute_with_ns: | From 39f77cead5962be7e8d7523968a703183ad3bef5 Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Thu, 13 Jun 2024 15:35:26 +0900 Subject: [PATCH 3/3] Update benchmark names --- benchmark/attribute.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/benchmark/attribute.yaml b/benchmark/attribute.yaml index 812329be..5dd7fded 100644 --- a/benchmark/attribute.yaml +++ b/benchmark/attribute.yaml @@ -34,7 +34,5 @@ prelude: | deepest_node = document.elements["//deepest"] benchmark: - attribute_with_ns: | - deepest_node.attribute("with_ns", "xyz") - attribute_without_ns: | - deepest_node.attribute("without_ns") + with_ns: deepest_node.attribute("with_ns", "xyz") + without_ns: deepest_node.attribute("without_ns")