Skip to content
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

C++ runtime: Make Parser::exitRule() non-virtual #2162

Merged
merged 4 commits into from
Nov 10, 2018

Conversation

janlinde
Copy link
Contributor

This patch aims at improving the performance of ANTLR-generated C++ parsers for large grammars.

Parser::exitRule() in the C++ runtime is a virtual function which is not reimplemented anywhere. OTOH, it is invoked during the execution of every rule, which seems to have a noticeable influence on performance. This commit removes its virtual qualifier. It should make a difference particularly for large grammars, because the number of rules corresponds to the number of the Parser object's virtual functions, and, consequently, its vtable lookup time.

Tested with a VHDL grammar of 436 rules, where it brings down parsing time from 75 to 44 seconds on unoptimized compilation, i.e. a 40% speed gain. Still a lot slower than an equivalent java parser, which takes 2.64 seconds for the same input.

Add missing override markers to the following functions of the C++
runtime:

  - TokensStartState::getStateType()
  - TagChunk::toString()
  - TextChunk::toString()

The missing markers made builds against the API with -Wsuggest-override
choke.
Parser::exitRule() in the C++ runtime is a virtual function which is not
reimplemented anywhere. OTOH, it is invoked during the execution of
every rule, which can cause a noticeable performance hit. This commit
removes its virtual qualifier. It should make a difference particularly
for large grammars, because the number of rules corresponds to the
number of the Parser object's virtual functions, and, consequently, its
vtable lookup time.

Tested with a VHDL grammar of 436 rules, where it brings down parsing
time from 75 to 44 seconds on unoptimized compilation, i.e. a 40% speed
gain.  Still a lot slower than an equivalent java parser, though, which
takes 2.64 seconds for the same input.
@mike-lischke
Copy link
Member

This is a very interesting outcome. VMT lookup shouldn't have such an impact and indeed I cannot reproduce this result with my (also fairly large grammar, > 500 parser rules, > 800 lexer rules). I used deeply nested expression to produce heavy recursion. But as I said, I can't reproduce that.

Could you attach your grammar and sample input here? I then can try with your input to see if that makes a difference.

@janlinde
Copy link
Contributor Author

VMT lookup shouldn't have such an impact and indeed I cannot reproduce this result

:-(. Did you compile with -O3 by chance? Which compiler do you use? I measured with GCC 7.2.1 from -O0 up to -O2, with -O3 the gap (nearly) closed, 44 seconds vs 40 or so, i.e. 10%.

Could you attach your grammar and sample input here? I then can try with your input to see if that
makes a difference.

I've attached it to an email to you. I'm iffy about publishing at this point, because it's still part of a commercial project.

@mike-lischke
Copy link
Member

mike-lischke commented Dec 16, 2017

I'm using clang (with XCode) and get these numbers (using your vhdl grammar):

Java:
Warmup time: 1.558 secs
Second parse: 0.556 secs

C++

without virtual:
Debug
Warmup time: 15.164 secs
Second parse time: 9.907 secs

Release
Warmup time: 8.131 secs
Second parse time: 5.301 secs

with 'virtual':
Debug
Warmup time: 15.594 secs
Second parse time: 10.099 secs

Release
Warmup time: 10.632 secs
Second parse time: 7.664 secs

That's a bit different than what I get with my MySQL grammar, so it seems to be related also to the grammar structure. Comparing the release build times gives us:

7.664 / 5.301 = 1.44576

hence ~45% speed increase (but 13 times slower than the Java parser, 10 times without virtual).

@mike-lischke
Copy link
Member

Here are the results for my MySQL grammar (using 3 simple but highly nested queries):

Java
Warmup time query 1: 707ms
Warmup time query 2: 8ms
Warmup time query 3: 529ms
Second parse query 1: 8ms
Second parse query 2: 3ms
Second parse query 3: 295ms

C++ (release only)
with virtual
Warmup time query 1: 1048.67 ms
Warmup time query 2: 6.677 ms
Warmup time query 3: 34.288 ms
Second parse query 1: 71.588 ms
Second parse query 2: 1.987 ms
Second parse query 3: 7.86 ms

w/o virtual
Warmup time query 1: 1046.18 ms
Warmup time query 2: 6.625 ms
Warmup time query 3: 34.976 ms
Second parse query 1: 72.825 ms
Second parse query 2: 1.864 ms
Second parse query 3: 7.939 ms

@parrt
Copy link
Member

parrt commented Nov 8, 2018

@mike-lischke should I merge?

@parrt parrt merged commit 40e883a into antlr:master Nov 10, 2018
@parrt parrt added this to the 4.7.2 milestone Nov 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants