From 0a5189054e4404fe13de7b0e10be5858648e0924 Mon Sep 17 00:00:00 2001 From: NAITOH Jun Date: Sat, 20 Jan 2024 07:03:11 +0900 Subject: [PATCH] Reduce calls to `Source#buffer`(`StringScanner#rest`) [Why] `Source#buffer` calling `StringScanner#rest`. `StringScanner#rest` is slow. Reduce calls to `Source#buffer`. ## Benchmark ``` RUBYLIB= BUNDLER_ORIG_RUBYLIB= /Users/naitoh/.rbenv/versions/3.3.0/bin/ruby -v -S benchmark-driver /Users/naitoh/ghq/github.com/naitoh/rexml/benchmark/parse.yaml ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22] Calculating ------------------------------------- before after before(YJIT) after(YJIT) dom 10.673 10.776 16.184 16.334 i/s - 100.000 times in 9.369090s 9.280170s 6.178928s 6.122305s sax 28.385 28.930 42.270 42.533 i/s - 100.000 times in 3.522933s 3.456645s 2.365771s 2.351098s pull 32.568 34.040 50.351 52.088 i/s - 100.000 times in 3.070514s 2.937748s 1.986055s 1.919817s stream 30.688 31.483 42.226 44.754 i/s - 100.000 times in 3.258605s 3.176358s 2.368228s 2.234450s Comparison: dom after(YJIT): 16.3 i/s before(YJIT): 16.2 i/s - 1.01x slower after: 10.8 i/s - 1.52x slower before: 10.7 i/s - 1.53x slower sax after(YJIT): 42.5 i/s before(YJIT): 42.3 i/s - 1.01x slower after: 28.9 i/s - 1.47x slower before: 28.4 i/s - 1.50x slower pull after(YJIT): 52.1 i/s before(YJIT): 50.4 i/s - 1.03x slower after: 34.0 i/s - 1.53x slower before: 32.6 i/s - 1.60x slower stream after(YJIT): 44.8 i/s before(YJIT): 42.2 i/s - 1.06x slower after: 31.5 i/s - 1.42x slower before: 30.7 i/s - 1.46x slower ``` - YJIT=ON : 1.01x - 1.06x faster - YJIT=OFF : 1.01x - 1.04x faster --- lib/rexml/parsers/baseparser.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb index 65bad260..be9c5677 100644 --- a/lib/rexml/parsers/baseparser.rb +++ b/lib/rexml/parsers/baseparser.rb @@ -349,8 +349,9 @@ def pull_event end begin @source.read if @source.buffer.size<2 - if @source.buffer[0] == ?< - if @source.buffer[1] == ?/ + pattern = @source.buffer[0,2] + if pattern[0] == ?< + if pattern[1] == ?/ @nsstack.shift last_tag = @tags.pop md = @source.match( CLOSE_MATCH, true ) @@ -364,7 +365,7 @@ def pull_event raise REXML::ParseException.new(message, @source) end return [ :end_element, last_tag ] - elsif @source.buffer[1] == ?! + elsif pattern[1] == ?! md = @source.match(/\A(\s*[^>]*>)/um) #STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}" raise REXML::ParseException.new("Malformed node", @source) unless md @@ -383,7 +384,7 @@ def pull_event end raise REXML::ParseException.new( "Declarations can only occur "+ "in the doctype declaration.", @source) - elsif @source.buffer[1] == ?? + elsif pattern[1] == ?? return process_instruction else # Get the next tag