-
Notifications
You must be signed in to change notification settings - Fork 76
Improve Performance with Nested CDATA #243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
naitoh
added a commit
to naitoh/rexml
that referenced
this issue
Mar 1, 2025
GitHub: fix rubyGH-243 ## Benchmark 1 ``` $ benchmark-driver benchmark/parse_cdata.yaml Calculating ------------------------------------- before after before(YJIT) after(YJIT) dom 99.008 192.055 96.030 177.653 i/s - 100.000 times in 1.010020s 0.520685s 1.041338s 0.562894s sax 101.858 205.223 99.488 194.657 i/s - 100.000 times in 0.981757s 0.487274s 1.005148s 0.513723s pull 99.614 205.030 100.284 196.212 i/s - 100.000 times in 1.003879s 0.487733s 0.997173s 0.509654s stream 102.506 205.073 100.324 195.893 i/s - 100.000 times in 0.975549s 0.487632s 0.996772s 0.510483s Comparison: dom after: 192.1 i/s after(YJIT): 177.7 i/s - 1.08x slower before: 99.0 i/s - 1.94x slower before(YJIT): 96.0 i/s - 2.00x slower sax after: 205.2 i/s after(YJIT): 194.7 i/s - 1.05x slower before: 101.9 i/s - 2.01x slower before(YJIT): 99.5 i/s - 2.06x slower pull after: 205.0 i/s after(YJIT): 196.2 i/s - 1.04x slower before(YJIT): 100.3 i/s - 2.04x slower before: 99.6 i/s - 2.06x slower stream after: 205.1 i/s after(YJIT): 195.9 i/s - 1.05x slower before: 102.5 i/s - 2.00x slower before(YJIT): 100.3 i/s - 2.04x slower ``` - YJIT=ON : 1.84x - 1.95x faster - YJIT=OFF : 1.94x - 2.04x faster ### Benchmark Code - benchmark/parse_cdata.yaml ```ruby loop_count: 100 contexts: - name: before gems: rexml: 3.4.1 require: false prelude: require 'rexml' - name: after prelude: | $LOAD_PATH.unshift(File.expand_path("lib")) require 'rexml' - name: before(YJIT) gems: rexml: 3.4.1 require: false prelude: | require 'rexml' RubyVM::YJIT.enable - name: after(YJIT) prelude: | $LOAD_PATH.unshift(File.expand_path("lib")) require 'rexml' RubyVM::YJIT.enable prelude: | require 'rexml/document' require 'rexml/parsers/sax2parser' require 'rexml/parsers/pullparser' require 'rexml/parsers/streamparser' require 'rexml/streamlistener' def build_xml(n_depth) xml = "<?xml version=\"1.0\"?>\n" + "<root>Test</root>\n" + "<!" + "[CDATA[" * n_depth + "]]>\n" end xml = build_xml(100000) class Listener include REXML::StreamListener end benchmark: 'dom' : REXML::Document.new(xml) 'sax' : REXML::Parsers::SAX2Parser.new(xml).parse 'pull' : | parser = REXML::Parsers::PullParser.new(xml) while parser.has_next? parser.pull end 'stream' : REXML::Parsers::StreamParser.new(xml, Listener.new).parse ``` ## Benchmark 2 - depth 140000001 : 1.91x faster - befofe ``` $ ruby nested_cdata.rb Trying depth 1 Elapsed time: 0.000148 seconds Trying depth 10000001 Elapsed time: 1.069345 seconds Trying depth 20000001 Elapsed time: 2.08041 seconds Trying depth 30000001 Elapsed time: 3.120498 seconds Trying depth 40000001 Elapsed time: 4.169959 seconds Trying depth 50000001 Elapsed time: 5.220279 seconds Trying depth 60000001 Elapsed time: 6.343796 seconds Trying depth 70000001 Elapsed time: 7.336923 seconds Trying depth 80000001 Elapsed time: 8.357979 seconds Trying depth 90000001 Elapsed time: 9.438741 seconds Trying depth 100000001 Elapsed time: 10.564045 seconds Trying depth 110000001 Elapsed time: 11.505168 seconds Trying depth 120000001 Elapsed time: 12.696258 seconds Trying depth 130000001 Elapsed time: 13.81369 seconds Trying depth 140000001 Elapsed time: 14.772476 seconds Trying depth 150000001 ^CError at depth 150000001: ``` - after ``` $ ruby nested_cdata.rb Trying depth 1 Elapsed time: 0.000135 seconds Trying depth 10000001 Elapsed time: 0.546576 seconds Trying depth 20000001 Elapsed time: 1.110255 seconds Trying depth 30000001 Elapsed time: 1.592627 seconds Trying depth 40000001 Elapsed time: 2.09499 seconds Trying depth 50000001 Elapsed time: 2.700936 seconds Trying depth 60000001 Elapsed time: 3.150459 seconds Trying depth 70000001 Elapsed time: 3.665467 seconds Trying depth 80000001 Elapsed time: 4.168667 seconds Trying depth 90000001 Elapsed time: 4.999614 seconds Trying depth 100000001 Elapsed time: 5.582072 seconds Trying depth 110000001 Elapsed time: 5.971595 seconds Trying depth 120000001 Elapsed time: 6.423684 seconds Trying depth 130000001 Elapsed time: 7.108247 seconds Trying depth 140000001 Elapsed time: 7.714127 seconds Trying depth 150000001 ^CError at depth 150000001: ``` ### Benchmark Code - nested_cdata.rb ```ruby require "rexml/document" include REXML (1..1000000000).step(10000000) do |depth| puts "Trying depth #{depth}" string = "<?xml version=\"1.0\"?>\n" + "<root>Test</root>\n" + "<!" + "[CDATA[" * depth + "]]>\n" start = Time.now begin doc = Document.new(string) rescue Exception => e puts "Error at depth #{depth}: #{e}" break end elapsed_time = Time.now - start puts "Elapsed time: #{elapsed_time} seconds" end ```
I don't think it is a problem. Large data takes time. Not related to nested cdata. size = 1000000
nested_cdata = "<?xml version=\"1.0\"?><root>Test</root><!" + "[CDATA[" * size + "]]>"
normal_cdata = "<?xml version=\"1.0\"?><root>Test</root><![CDATA[" + 'aaaaaaa' * size + "]]>"
normal_text = "<?xml version=\"1.0\"?><root>Test" + 'aaaaaaa' * size + "</root>"
normal_tags = "<?xml version=\"1.0\"?><root>Test" + '<b></b>' * size + "</root>"
# Size are almost the same.
[nested_cdata, normal_cdata, normal_text, normal_tags].map(&:size)
# => [7000043, 7000050, 7000038, 7000038]
REXML::Document.new(nested_cdata);
# processing time: 0.130978s
REXML::Document.new(normal_cdata);
# processing time: 0.109913s
REXML::Document.new(normal_text);
# processing time: 0.144391s
REXML::Document.new(normal_tags);
# processing time: 5.863289s The slowness is just caused by the input size. Time consumption of parsing nested cdata, non-nested normal cdata, normal text node is almost the same.
|
naitoh
added a commit
to naitoh/rexml
that referenced
this issue
Mar 2, 2025
## Why? See: ruby#243 ## Benchmark (Comparison with rexml 3.4.1) ``` $ benchmark-driver benchmark/parse_cdata.yaml Calculating ------------------------------------- rexml 3.4.1 master 3.4.1(YJIT) master(YJIT) dom 648.361 1.178k 591.590 1.046k i/s - 100.000 times in 0.154235s 0.084913s 0.169036s 0.095627s sax 699.061 1.378k 651.148 1.196k i/s - 100.000 times in 0.143049s 0.072549s 0.153575s 0.083611s pull 699.271 1.379k 660.275 1.210k i/s - 100.000 times in 0.143006s 0.072527s 0.151452s 0.082622s stream 701.725 1.383k 659.483 1.228k i/s - 100.000 times in 0.142506s 0.072307s 0.151634s 0.081455s Comparison: dom master: 1177.7 i/s master(YJIT): 1045.7 i/s - 1.13x slower rexml 3.4.1: 648.4 i/s - 1.82x slower 3.4.1(YJIT): 591.6 i/s - 1.99x slower sax master: 1378.4 i/s master(YJIT): 1196.0 i/s - 1.15x slower rexml 3.4.1: 699.1 i/s - 1.97x slower 3.4.1(YJIT): 651.1 i/s - 2.12x slower pull master: 1378.8 i/s master(YJIT): 1210.3 i/s - 1.14x slower rexml 3.4.1: 699.3 i/s - 1.97x slower 3.4.1(YJIT): 660.3 i/s - 2.09x slower stream master: 1383.0 i/s master(YJIT): 1227.7 i/s - 1.13x slower rexml 3.4.1: 701.7 i/s - 1.97x slower 3.4.1(YJIT): 659.5 i/s - 2.10x slower ``` - YJIT=ON : 1.76x - 1.83x faster - YJIT=OFF : 1.82x - 1.97x faster
naitoh
added a commit
to naitoh/rexml
that referenced
this issue
Mar 2, 2025
## Why? GitHub: fix ruby#243 ## Benchmark (Comparison with rexml 3.4.1) ``` $ benchmark-driver benchmark/parse_cdata.yaml Calculating ------------------------------------- rexml 3.4.1 master 3.4.1(YJIT) master(YJIT) dom 648.361 1.178k 591.590 1.046k i/s - 100.000 times in 0.154235s 0.084913s 0.169036s 0.095627s sax 699.061 1.378k 651.148 1.196k i/s - 100.000 times in 0.143049s 0.072549s 0.153575s 0.083611s pull 699.271 1.379k 660.275 1.210k i/s - 100.000 times in 0.143006s 0.072527s 0.151452s 0.082622s stream 701.725 1.383k 659.483 1.228k i/s - 100.000 times in 0.142506s 0.072307s 0.151634s 0.081455s Comparison: dom master: 1177.7 i/s master(YJIT): 1045.7 i/s - 1.13x slower rexml 3.4.1: 648.4 i/s - 1.82x slower 3.4.1(YJIT): 591.6 i/s - 1.99x slower sax master: 1378.4 i/s master(YJIT): 1196.0 i/s - 1.15x slower rexml 3.4.1: 699.1 i/s - 1.97x slower 3.4.1(YJIT): 651.1 i/s - 2.12x slower pull master: 1378.8 i/s master(YJIT): 1210.3 i/s - 1.14x slower rexml 3.4.1: 699.3 i/s - 1.97x slower 3.4.1(YJIT): 660.3 i/s - 2.09x slower stream master: 1383.0 i/s master(YJIT): 1227.7 i/s - 1.13x slower rexml 3.4.1: 701.7 i/s - 1.97x slower 3.4.1(YJIT): 659.5 i/s - 2.10x slower ``` - YJIT=ON : 1.76x - 1.83x faster - YJIT=OFF : 1.82x - 1.97x faster Co-authored-by: Sutou Kouhei <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think we should improve the implementation to handle nested CDATA more efficiently.
Result:
The text was updated successfully, but these errors were encountered: