antlr4
antlr4 copied to clipboard
Constant folding for Python and other runtimes
@SimonStPeter I've created a benchmark with respective grammar and have got the following result:
no_const_folding: 2010734 ns
const_folding: 907140 ns
ratio: 0.4511
I haven't tested on real grammars, but It's already signficant improvement, more 2x faster and it should be considered. Also, resulting file > 20 % less than the file with not folded constants.
There is no improvement in speed for JavaScript.
fixes #3698, fixes #3699
@parrt I suggest using constant folding for all runtimes because:
- It reduces the size of generated parsers (more than 20% as I've checked for Python and JavaScript)
- It eliminates very long lines that are hard to read by human and text editors. Just compare (there are a lot of similar lines for SQL parsers):
- Current:
if not(((((_la - 1)) & ~0x3f) == 0 and ((1 << (_la - 1)) & ((1 << (PParser.T0 - 1)) | (1 << (PParser.T1 - 1)) | (1 << (PParser.T2 - 1)) | (1 << (PParser.T3 - 1)) | (1 << (PParser.T4 - 1)) | (1 << (PParser.T5 - 1)) | (1 << (PParser.T6 - 1)) | (1 << (PParser.T7 - 1)) | (1 << (PParser.T8 - 1)) | (1 << (PParser.T9 - 1)) | (1 << (PParser.T10 - 1)) | (1 << (PParser.T11 - 1)) | (1 << (PParser.T12 - 1)) | (1 << (PParser.T13 - 1)) | (1 << (PParser.T14 - 1)) | (1 << (PParser.T15 - 1)) | (1 << (PParser.T16 - 1)) | (1 << (PParser.T17 - 1)) | (1 << (PParser.T18 - 1)) | (1 << (PParser.T19 - 1)) | (1 << (PParser.T20 - 1)) | (1 << (PParser.T21 - 1)) | (1 << (PParser.T22 - 1)) | (1 << (PParser.T23 - 1)) | (1 << (PParser.T24 - 1)) | (1 << (PParser.T25 - 1)) | (1 << (PParser.T26 - 1)) | (1 << (PParser.T27 - 1)) | (1 << (PParser.T28 - 1)) | (1 << (PParser.T29 - 1)) | (1 << (PParser.T30 - 1)) | (1 << (PParser.T31 - 1)) | (1 << (PParser.T32 - 1)) | (1 << (PParser.T33 - 1)) | (1 << (PParser.T34 - 1)) | (1 << (PParser.T35 - 1)) | (1 << (PParser.T36 - 1)) | (1 << (PParser.T37 - 1)) | (1 << (PParser.T38 - 1)) | (1 << (PParser.T39 - 1)) | (1 << (PParser.T40 - 1)) | (1 << (PParser.T41 - 1)) | (1 << (PParser.T42 - 1)) | (1 << (PParser.T43 - 1)) | (1 << (PParser.T44 - 1)) | (1 << (PParser.T45 - 1)) | (1 << (PParser.T46 - 1)) | (1 << (PParser.T47 - 1)) | (1 << (PParser.T48 - 1)) | (1 << (PParser.T49 - 1)) | (1 << (PParser.T50 - 1)) | (1 << (PParser.T51 - 1)) | (1 << (PParser.T52 - 1)) | (1 << (PParser.T53 - 1)) | (1 << (PParser.T54 - 1)) | (1 << (PParser.T55 - 1)) | (1 << (PParser.T56 - 1)) | (1 << (PParser.T57 - 1)) | (1 << (PParser.T58 - 1)) | (1 << (PParser.T59 - 1)) | (1 << (PParser.T60 - 1)) | (1 << (PParser.T61 - 1)) | (1 << (PParser.T62 - 1)) | (1 << (PParser.T63 - 1)))) != 0)):
- Folded:
if not((((_la - 1) & ~0x3f) == 0 and (1 << (_la - 1)) & -1) != 0):
- Current:
- Actually such expressions almost don't clarity how parser works in debug mode (if it's important).
- It improves compilation speed (for compilable languages) or interpretation performance (for dynamic languages such as Python or JavaScript)
CI improvement for Python: 12s
vs >13s
vs (>10%)
Looks cool. I'll check as soon as I can.
Good stuff. I'd be really interested to see how it works on a large grammar. If you've fixed the python perf problem that would be Most Excellent Indeed. Fingers crossed.
cheers
jan
On 07/05/2022 21:02, Ivan Kochurkin wrote:
@SimonStPeter https://github.com/SimonStPeter I've created a bechmark https://github.com/KvanTTT/AntlrBenchmarks/blob/master/ConstantFoldingBenchmark/checkParsers.py with respective grammar and have got the following result:
|no_const_folding: 2010734 ns const_folding: 907140 ns ratio: 0.4511 |
I haven't tested on real grammars but It's already signficant improvement, more 2x faster and it should be considered. Also, resulting file > 20 % less than the file with not folded constants.
There is no improvement in speed for JavaScript.
fixes #3698 https://github.com/antlr/antlr4/issues/3698, fixes #3699 https://github.com/antlr/antlr4/issues/3699
@parrt https://github.com/parrt I suggest use constant folding for all runtimes because:
- It reduces the size of generated parsers (more than 20% as I've checked for Python and JavaScript)
- It eliminates very long lines that are hard to read by human and text editors. Just compare: o Current: |if not(((((_la - 1)) & ~0x3f) == 0 and ((1 << (_la - 1)) & ((1 << (PParser.T0 - 1)) | (1 << (PParser.T1 - 1)) | (1 << (PParser.T2 - 1)) | (1 << (PParser.T3 - 1)) | (1 << (PParser.T4 - 1)) | (1 << (PParser.T5 - 1)) | (1 << (PParser.T6 - 1)) | (1 << (PParser.T7 - 1)) | (1 << (PParser.T8 - 1)) | (1 << (PParser.T9 - 1)) | (1 << (PParser.T10 - 1)) | (1 << (PParser.T11 - 1)) | (1 << (PParser.T12 - 1)) | (1 << (PParser.T13 - 1)) | (1 << (PParser.T14 - 1)) | (1 << (PParser.T15 - 1)) | (1 << (PParser.T16 - 1)) | (1 << (PParser.T17 - 1)) | (1 << (PParser.T18 - 1)) | (1 << (PParser.T19 - 1)) | (1 << (PParser.T20 - 1)) | (1 << (PParser.T21 - 1)) | (1 << (PParser.T22 - 1)) | (1 << (PParser.T23 - 1)) | (1 << (PParser.T24 - 1)) | (1 << (PParser.T25 - 1)) | (1 << (PParser.T26 - 1)) | (1 << (PParser.T27 - 1)) | (1 << (PParser.T28 - 1)) | (1 << (PParser.T29 - 1)) | (1 << (PParser.T30 - 1)) | (1 << (PParser.T31 - 1)) | (1 << (PParser.T32 - 1)) | (1 << (PParser.T33 - 1)) | (1 << (PParser.T34 - 1)) | (1 << (PParser.T35 - 1)) | (1 << (PParser.T36 - 1)) | (1 << (PParser.T37 - 1)) | (1 << (PParser.T38 - 1)) | (1 << (PParser.T39 - 1)) | (1 << (PParser.T40 - 1)) | (1 << (PParser.T41 - 1)) | (1 << (PParser.T42 - 1)) | (1 << (PParser.T43 - 1)) | (1 << (PParser.T44 - 1)) | (1 << (PParser.T45 - 1)) | (1 << (PParser.T46 - 1)) | (1 << (PParser.T47 - 1)) | (1 << (PParser.T48 - 1)) | (1 << (PParser.T49 - 1)) | (1 << (PParser.T50 - 1)) | (1 << (PParser.T51 - 1)) | (1 << (PParser.T52 - 1)) | (1 << (PParser.T53 - 1)) | (1 << (PParser.T54 - 1)) | (1 << (PParser.T55 - 1)) | (1 << (PParser.T56 - 1)) | (1 << (PParser.T57 - 1)) | (1 << (PParser.T58 - 1)) | (1 << (PParser.T59 - 1)) | (1 << (PParser.T60 - 1)) | (1 << (PParser.T61 - 1)) | (1 << (PParser.T62 - 1)) | (1 << (PParser.T63 - 1)))) != 0)):| o Folded: |if not((((_la - 1) & ~0x3f) == 0 and (1 << (_la - 1)) & -1) != 0):|
- Actually such expressions don't clarity how parser works in debug mode.
- It improves compilation speed (for compilable languages) or interpretation performance (for Python)
You can view, comment on, or merge this pull request online at:
https://github.com/antlr/antlr4/pull/3701 https://github.com/antlr/antlr4/pull/3701
Commit Summary
f4b2fe0 https://github.com/antlr/antlr4/pull/3701/commits/f4b2fe0d384ef10994aa5aa84cb79d9d4f6429e6 Fold constants for Python and JavaScript runtimes, fixes #3698, fixes #3699
File Changes
(5 files https://github.com/antlr/antlr4/pull/3701/files)
M runtime-testsuite/test/org/antlr/v4/test/runtime/GeneratedLexerDescriptors.java https://github.com/antlr/antlr4/pull/3701/files#diff-859a1117c3f13396162959e824858c01d179a61f3280ab64fd68c9c9490b2729 (38)
M tool/resources/org/antlr/v4/tool/templates/codegen/JavaScript/JavaScript.stg https://github.com/antlr/antlr4/pull/3701/files#diff-3a264c84c42865f82b0840e2dbacb7bc141d8bc92b7199f3cc14780063b17813 (4)
M tool/resources/org/antlr/v4/tool/templates/codegen/Python2/Python2.stg https://github.com/antlr/antlr4/pull/3701/files#diff-5704e73d866662c1b798f5f182bc3b42d0d4bd65623c1b6b288acc7fbe510afd (4)
M tool/resources/org/antlr/v4/tool/templates/codegen/Python3/Python3.stg https://github.com/antlr/antlr4/pull/3701/files#diff-5095eaa7b541d61912c44e0261e6b1907b71b6f48a7b1eb7d997c233d2de0b47 (4)
M tool/src/org/antlr/v4/codegen/model/TestSetInline.java https://github.com/antlr/antlr4/pull/3701/files#diff-8a7295a1afd0032c88ec98f46531cf0c4a8428826e0402329ad916d4c81f2184 (44)
Patch Links:
https://github.com/antlr/antlr4/pull/3701.patch https://github.com/antlr/antlr4/pull/3701.patch
https://github.com/antlr/antlr4/pull/3701.diff https://github.com/antlr/antlr4/pull/3701.diff
— Reply to this email directly, view it on GitHub https://github.com/antlr/antlr4/pull/3701, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACNF57LFP2NPDHRHRTYMKZTVI3D4VANCNFSM5VK3FXWQ. You are receiving this because you were mentioned.Message ID: @.***>
Also I've completed the following:
-
Go
: getInlineTestSetWordSize 32 -> 64 (go actually supports long integers) -
Dart
: get rid of BigInt -
Swift
: optimize TestSetInline (also should improve performance)
Should we look at this again after completing your other testing upgrades? @KvanTTT
Yes, I think it makes sense to get rid of very long lines in generated sources.
Eventually, it's ready for review.
@parrt any chance it will be merged?
Happy to merge once you add the parentheses and change the name of the method! thanks!
Eventually done. Some fails on C# tests, but are unrelated.
Eventually done. Some fails on C# tests, but are unrelated.
rerunning C# now...
Nice work! I'll merge it in and then verify the Java gen'd code still looks OK haha
It's arguably being 'evaluated' so perhaps 'folding' -> 'evaluation'.
Also these are constants, but more precisely I'd say they are literal constants, or literals for short. So maybe 'constant' -> 'literal'
jan
On 28/08/2022 12:23, Ivan Kochurkin wrote:
@.**** commented on this pull request.
In tool/src/org/antlr/v4/codegen/model/Choice.java https://github.com/antlr/antlr4/pull/3701#discussion_r956711633:
for (IntervalSet s : altLookSets) {
altLook.add(factory.getGenerator().getTarget().getTokenTypesAsTargetLabels(factory.getGrammar(), s.toArray()));
if (target.supportsConstants()) {
Not sure it's correct naming. Python supports constant folding, i.e. the following expression
|(1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | \ (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12) | \ (1 << 13) | (1 << 14) | (1 << 15) | (1 << 16) |
is being folded to constant |0xFFFF|. But Python doesn't support constant propagation since Python is unable to determine whether a variable is constant or not.
— Reply to this email directly, view it on GitHub https://github.com/antlr/antlr4/pull/3701#discussion_r956711633, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACNF57NDAG5JGNVYEKXFX53V3ND4BANCNFSM5VK3FXWQ. You are receiving this because you were mentioned.Message ID: @.***>
@KvanTTT I have a warning from C++ where stuff like:
((_la & ~ 0x3fULL) == 0) &&
((1ULL << _la) & -41056) != 0 || (((_la - 64) & ~ 0x3fULL) == 0) &&
((1ULL << (_la - 64)) & 32767) != 0
gets warnings
$ clang++ -Werror,-Wlogical-op-parentheses t.cc
warning: unknown -Werror warning specifier: '-Werror,-Wlogical-op-parentheses' [-Wunknown-warning-option]
1 warning generated.
parrt-macbookpro2:tmp parrt$ clang++ -Werror -Wlogical-op-parentheses t.cc
t.cc:3:34: error: '&&' within '||' [-Werror,-Wlogical-op-parentheses]
if (((_la & ~ 0x3fULL) == 0) &&
~~~~~~~~~~~~~~~~~~~~~~~~~^~
t.cc:3:34: note: place parentheses around the '&&' expression to silence this warning
if (((_la & ~ 0x3fULL) == 0) &&
^
The -41056
makes me nervous. Are you sure that word size is portably correct, given the ULL usage?
Seems like C++ would change this template to wrap in parens:
bitsetBitfieldComparison(s, bits) ::= <<
<testShiftInRange({<offsetShift(s.varName, bits.shift)>})> &&
((1ULL \<\< <offsetShift(s.varName, bits.shift)>) & <bits.calculated>) != 0
>>
Some companies flip this warning to an error.