> > Parsing with ANTLR4 generated parsers is very sensitive to both the grammar and the input.
> The more surprising it is that I can't find the guide that would tell "do this" and "avoid that" for ANTLR4 grammars.
Yes true. I thought about starting a list for that more than once. But where to keep it? Sam could say a lot about speed optimization, much more than anyone else and this knowledge should be saved.
> In old ANTLR all was clear - if you needed syntactic predicate this was the point where you could have performance problems for some inputs.
Terence wrote somewhere that ANTLR4 is not primarily optimized for speed, but for convenience. After all it's an academic project that just happen to fit quite well for many real world products. A fixed lookahead like in ANTLR3 is certainly faster than the dynamic lookahead in ANLTR4 (and less memory consuming), but fails for certain languages with ambiguities (and there are unfortunately many).
> In new ANTLR everything falls into magic box of ParserATNSimulator::adaptivePredict. The magic is great when it works, but when it stops working or runs too slow the real problem starts. It is also much easier to misuse magic than plain solution.
Misusing that is likely not a problem here, since it is an internal function. However, from a programming standpoint it's a nightmare. The prediction is highly recursive and can created huge stacks for complex input. Due to the way ATN configs and states are shared there is an extra challenge for languages without full automatic memory management (like C++) and requires there extra (time costly) code to avoid memory leaks. Btw, I believe the use of shared_ptr in the C++ runtime can still be optimized and the closure()/closure_()/closureCheckingStopState() triumvirate should be rewritten as a loop (not only in C++ however).
>
> > All these numbers, have they been taken after warmup (the first parse run)?
> In my case there is never a second parse run.
Well, even multiple instances of a parser count as multiple runs (as long as the entire application stays alive). The ATN (and the generated DFA) are static and shared among all parser instances for the same grammar. So, once you parsed certain input and the internal structure has been created for that it will be much faster on the next parse run (regardless which parser instance does this). In order to make this thread safe there are a few locks which also cost a bit time. I recommend to do some tests with a single parser on a single thread and run this twice for the same input, measuring only the second run. This should give you a good idea about the normal parsing speed.
> For Java version I'm just running TestRig built into ANTLR with no options or with just -SLL. For C++ the flow is the following: lexer(s) -> flyweight like file structure -> preprocessor -> filter -> actual parser specific token source -> parser. Everything up to parser token source is shared between old ANTLR2 and new ANTLR4 implementations. In normal simulator run once parser finishes its work all the generated VPI structures are run through basic checks and serialized to library, parser is destroyed and that's it.
I hope this doesn't involve releasing the static data (by closing the app that parsed the input). If so you are using ANTLR4 in a worst case scenario.
> Further stages load all they need from libraries (they can in fact be run in a completely separate process). In other words one simulator execution equals one parse (or no parse at all if no sources changed since last run). There are other rare options for running multiple parses in one go but default is to glue all input files together (all macros and directives are globally visible exactly as if all files were concatenated) so even if there are multiple input files there is always just one parse.
If that happens in a process that is never closed (and hence all static data stays alive) it should not be a problem.
Mike
--
www.soft-gems.net