Skip to content

Commit 9619325

Browse files
committed
Optimization of REXML::Document#add, #add_element
## Benchmark ``` $ benchmark-driver benchmark/parse_comment.yaml before after before(YJIT) after(YJIT) top_level 48.497k 54.645k 7.951k 8.288k i/s - 100.000 times in 0.002062s 0.001830s 0.012577s 0.012066s in_doctype 48.615k 50.994k 7.930k 7.820k i/s - 100.000 times in 0.002057s 0.001961s 0.012611s 0.012787s after_doctype 63.251k 64.061k 9.569k 9.434k i/s - 100.000 times in 0.001581s 0.001561s 0.010450s 0.010600s many_comments 67.653 611.015 117.191 929.290 i/s - 100.000 times in 1.478125s 0.163662s 0.853309s 0.107609s Comparison: top_level after: 54644.8 i/s before: 48496.6 i/s - 1.13x slower after(YJIT): 8287.7 i/s - 6.59x slower before(YJIT): 7951.0 i/s - 6.87x slower in_doctype after: 50994.4 i/s before: 48614.5 i/s - 1.05x slower before(YJIT): 7929.6 i/s - 6.43x slower after(YJIT): 7820.4 i/s - 6.52x slower after_doctype after: 64061.5 i/s before: 63251.1 i/s - 1.01x slower before(YJIT): 9569.4 i/s - 6.69x slower after(YJIT): 9434.0 i/s - 6.79x slower many_comments after(YJIT): 929.3 i/s after: 611.0 i/s - 1.52x slower before(YJIT): 117.2 i/s - 7.93x slower before: 67.7 i/s - 13.74x slower ``` - YJIT=ON : 0.98 - 7.93x faster - YJIT=OFF : 1.01 - 9.02x faster
1 parent 5248d63 commit 9619325

3 files changed

Lines changed: 51 additions & 7 deletions

File tree

benchmark/parse_comment.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ contexts:
2424
prelude: |
2525
require 'rexml/document'
2626
27-
SIZE = 100000
27+
SIZE = 1000
2828
2929
top_level_xml = "<!--" + "a" * SIZE + "-->\n<root/>"
3030
in_doctype_xml = "<!DOCTYPE foo [<!--" + "a" * SIZE + "-->]><root/>"
3131
after_doctype_xml = "<root/><!--" + "a" * SIZE + "-->"
32+
many_comments_xml = "<!---->" * SIZE + "<a/>"
3233
3334
benchmark:
3435
'top_level' : REXML::Document.new(top_level_xml)
3536
'in_doctype' : REXML::Document.new(in_doctype_xml)
3637
'after_doctype' : REXML::Document.new(after_doctype_xml)
38+
'many_comments' : REXML::Document.new(many_comments_xml)

lib/rexml/document.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,8 @@ def add( child )
197197
end
198198
child.parent = self
199199
else
200-
rv = super
201-
raise "attempted adding second root element to document" if @elements.size > 1
202-
rv
200+
raise "attempted adding second root element to document" if child.kind_of?(Element) && root
201+
super
203202
end
204203
end
205204
alias :<< :add
@@ -211,9 +210,8 @@ def add( child )
211210
#
212211
# REXML::Element.add_element(name_or_element, attributes)
213212
def add_element(arg=nil, arg2=nil)
214-
rv = super
215-
raise "attempted adding second root element to document" if @elements.size > 1
216-
rv
213+
raise "attempted adding second root element to document" if root
214+
super
217215
end
218216

219217
# :call-seq:

test/test_document.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,50 @@ def test_encoding
320320
end
321321
end
322322

323+
class AddTest < Test::Unit::TestCase
324+
def test_add_second_root_element_raises
325+
doc = REXML::Document.new("<root/>")
326+
assert_raise(RuntimeError, "attempted adding second root element to document") do
327+
doc.add(REXML::Element.new("second"))
328+
end
329+
end
330+
331+
def test_append_operator_second_root_element_raises
332+
doc = REXML::Document.new("<root/>")
333+
assert_raise(RuntimeError, "attempted adding second root element to document") do
334+
doc << REXML::Element.new("second")
335+
end
336+
end
337+
338+
def test_add_element_second_root_raises
339+
doc = REXML::Document.new("<root/>")
340+
assert_raise(RuntimeError, "attempted adding second root element to document") do
341+
doc.add_element("second")
342+
end
343+
end
344+
345+
def test_add_element_with_element_second_root_raises
346+
doc = REXML::Document.new("<root/>")
347+
assert_raise(RuntimeError, "attempted adding second root element to document") do
348+
doc.add_element(REXML::Element.new("second"))
349+
end
350+
end
351+
352+
def test_add_xml_decl_allowed
353+
doc = REXML::Document.new("<root/>")
354+
assert_nothing_raised do
355+
doc.add(REXML::XMLDecl.new("1.0"))
356+
end
357+
end
358+
359+
def test_add_doctype_allowed
360+
doc = REXML::Document.new("<root/>")
361+
assert_nothing_raised do
362+
doc.add(REXML::DocType.new("root"))
363+
end
364+
end
365+
end
366+
323367
class BomTest < Test::Unit::TestCase
324368
class HaveEncodingTest < self
325369
def test_utf_8

0 commit comments

Comments
 (0)