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

cache ATNConfig hashCode in Java, C#, Python and JavaScript #4546

Closed

Conversation

ericvergnaud
Copy link
Contributor

This proposal follows findings in mike-lischke/antlr4ng@77d0d47 where caching ATNConfig hashCode seems to improve performance significantly during warmup

Signed-off-by: Eric Vergnaud <eric.vergnaud@wanadoo.fr>
@ericvergnaud
Copy link
Contributor Author

@kaby76 Do you have a benchmark we could use to see if this change genuinely improves performance, and at what cost (memory) ?

@kaby76
Copy link
Contributor

kaby76 commented Mar 4, 2024

@kaby76 Do you have a benchmark we could use to see if this change genuinely improves performance, and at what cost (memory) ?

I don't have any scripts offhand to compare the performance between before and after versions of the Antlr runtime. It'll have to be done by hand, or a script developed. But, trgen could be used to run the test. Let me write something up.

@kaby76
Copy link
Contributor

kaby76 commented Mar 4, 2024

@kaby76 Do you have a benchmark we could use to see if this change genuinely improves performance, and at what cost (memory) ?

For grouped parsing of the 373 files for sql/plsql, 4.13.1 CSharp target vs your PR, there was no improvement. In fact, it was slightly worse on a couple of runs. I double checked everything. I think this may be because Net Core likely caches hash codes.

It may help with TypeScript, but I haven't yet checked it.

@ericvergnaud
Copy link
Contributor Author

ericvergnaud commented Mar 4, 2024 via email

@kaby76
Copy link
Contributor

kaby76 commented Mar 5, 2024

I don't see any improvements with the TypeScript target either for the cpp grammar.

Have you seen a perf increase with these changes?

@ericvergnaud
Copy link
Contributor Author

ericvergnaud commented Mar 5, 2024 via email

@kaby76
Copy link
Contributor

kaby76 commented Mar 5, 2024

Supposedly faster. But, I cannot reproduce Mike's result. Here's my code in a zip file.
java.zip

It doesn't run any faster at least how I'm building and running the code. But, nobody ever seems to want to write tools to generate a working example start to finish of a parser with driver from a grammar.

Maybe there may be special settings and versions of node that are presumed to perform optimizations?

In my generated example for java/java/, Antlr4 parses the 14 files in grouped parse mode aka "warm up" faster than Antlr4ng.

03/05-03:57:23 ~/test/grammars-v4/java/java/Generated-TypeScript
$ bash run.sh ../examples/*.java
TypeScript 0 ../examples/AllInOne11.java success 0.663
TypeScript 1 ../examples/AllInOne17.java success 0.904
TypeScript 2 ../examples/AllInOne7.java success 0.334
TypeScript 3 ../examples/AllInOne8.java success 0.534
TypeScript 4 ../examples/ConsecutiveSemicolons.java success 0.001
TypeScript 5 ../examples/Escapes.java success 0.005
TypeScript 6 ../examples/ExpressionOrder.java success 0.03
TypeScript 7 ../examples/GenericConstructor.java success 0.004
TypeScript 8 ../examples/LocalVariableDeclaration.java success 0.009
TypeScript 9 ../examples/ManyStringsConcat.java success 0.537
TypeScript 10 ../examples/module-info.java success 0.002
TypeScript 11 ../examples/RecordsTesting.java success 0.011
TypeScript 12 ../examples/SwitchExpression.java success 0.008
TypeScript 13 ../examples/TryStatements.java success 0
Total Time: 3.069
03/05-03:57:55 ~/test/grammars-v4/java/java/Generated-Antlr4ng
$ bash run.sh ../examples/*.java
TypeScript 0 ../examples/AllInOne11.java success 0.686
TypeScript 1 ../examples/AllInOne17.java success 0.925
TypeScript 2 ../examples/AllInOne7.java success 0.34
TypeScript 3 ../examples/AllInOne8.java success 0.559
TypeScript 4 ../examples/ConsecutiveSemicolons.java success 0
TypeScript 5 ../examples/Escapes.java success 0.003
TypeScript 6 ../examples/ExpressionOrder.java success 0.037
TypeScript 7 ../examples/GenericConstructor.java success 0.006
TypeScript 8 ../examples/LocalVariableDeclaration.java success 0.008
TypeScript 9 ../examples/ManyStringsConcat.java success 0.764
TypeScript 10 ../examples/module-info.java success 0.002
TypeScript 11 ../examples/RecordsTesting.java success 0.012
TypeScript 12 ../examples/SwitchExpression.java success 0.008
TypeScript 13 ../examples/TryStatements.java success 0.003
Total Time: 3.374

People can make claims, but unless a .zip file is provided that includes the source, drivers, build files, etc etc etc, I don't trust it. There is my code. Why is Antlr4ng slower in it? Someone, tell me what is wrong with this code or prove with a .zip that I can run showing the claim.

@ericvergnaud
Copy link
Contributor Author

Maybe it's only faster for the MySQL grammar...
Not sure what you mean by .json driver ? Should we create an issue in antlr5 in order to address this topic ?

@kaby76
Copy link
Contributor

kaby76 commented Mar 5, 2024

I will check it for the mysql grammar.

@kaby76
Copy link
Contributor

kaby76 commented Mar 5, 2024

Here's a run of the mysql parser grammar on bitrix_queries_cut.sql. The parse is performed twice on same input. The first time is the "before warm up", the second time is the "after warm up":

03/05-04:50:21 ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-Antlr4ng
$ bash run.sh ../examples/b*.sql ../examples/b*.sql
TypeScript 0 ../examples/bitrix_queries_cut.sql success 186.266
TypeScript 1 ../examples/bitrix_queries_cut.sql success 168.398
Total Time: 354.68
03/05-04:56:19 ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-Antlr4ng
$ pushd
~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-TypeScript ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-Antlr4ng
03/05-04:56:29 ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-TypeScript
$ bash run.sh ../examples/b*.sql ../examples/b*.sql
TypeScript 0 ../examples/bitrix_queries_cut.sql success 160.187
TypeScript 1 ../examples/bitrix_queries_cut.sql success 145.515
Total Time: 305.716
03/05-05:01:41 ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-TypeScript
$

Antlr4ng is slower than the original 4.13.1 TypeScript runtime.

Positive-Technologies.zip

Note, the Oracle mysql grammar is not in grammars-v4. This is the alternative, non-Oracle mysql grammar. But, in all the tests I've done, Antlr4ng 3.0.3/Antlr4ng-cli 2.0.0 is slower than Antlr 4.13.1 TypeScript. I'm not sure what to make of the claim at this point.

Going back to https://github.com/mike-lischke/antlr4ng?tab=readme-ov-file#running-the-tests , I try to replicate his tests, but it doesn't work at all:

03/05-05:20:08 ~/test/mike
$ ls
03/05-05:20:09 ~/test/mike
$ npm run generate-test-parsers
npm ERR! code ENOENT
npm ERR! syscall open
npm ERR! path C:\msys64\home\Kenne\test\mike/package.json
npm ERR! errno -4058
npm ERR! enoent ENOENT: no such file or directory, open 'C:\msys64\home\Kenne\test\mike\package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\Kenne\AppData\Local\npm-cache\_logs\2024-03-05T10_20_15_395Z-debug-0.log
03/05-05:20:15 ~/test/mike
$
$ git clone https://github.com/mike-lischke/antlr4ng.git
Cloning into 'antlr4ng'...
remote: Enumerating objects: 2924, done.
remote: Counting objects: 100% (1339/1339), done.
remote: Compressing objects: 100% (403/403), done.
remote: Total 2924 (delta 1044), reused 1043 (delta 932), pack-reused 1585
Receiving objects: 100% (2924/2924), 6.36 MiB | 18.45 MiB/s, done.
Resolving deltas: 100% (2078/2078), done.
Updating files: 100% (236/236), done.
03/05-05:21:55 ~/test/mike
$ cd antlr4ng/
03/05-05:22:06 ~/test/mike/antlr4ng
$ npm run generate-test-parsers

> antlr4ng@3.0.3 generate-test-parsers
> ./build/generate-test-parsers.sh

'.' is not recognized as an internal or external command,
operable program or batch file.
03/05-05:22:09 ~/test/mike/antlr4ng
$
$ cat build/generate-test-parsers.sh

printf "\x1b[1m\x1b[34mGenerating test parsers...\x1b[0m\n\n"

# Temporarily copy the ANTLR4 jar to the antlr4ng-cli directory, to allow using a newer version, not yet published to npm.
# cp cli/antlr4-4.13.2-SNAPSHOT-complete.jar node_modules/antlr4ng-cli/antlr4-4.13.2-SNAPSHOT-complete.jar

java -jar cli/antlr4-4.13.2-SNAPSHOT-complete.jar -Dlanguage=TypeScript -visitor -Xexact-output-dir -o ./tests/generated ./tests/fixtures/grammars/*.g4
java -jar cli/antlr4-4.13.2-SNAPSHOT-complete.jar -Dlanguage=TypeScript -o tests/benchmarks/generated -visitor -listener -Xexact-output-dir tests/benchmarks/MySQLLexer.g4 tests/benchmarks/MySQLParser.g4

printf "done\n\n"
03/05-05:23:49 ~/test/mike/antlr4ng
$ bash build/generate-test-parsers.sh
Generating test parsers...

done

03/05-05:24:28 ~/test/mike/antlr4ng
$ npm run generate-runtime-tests

> antlr4ng@3.0.3 generate-runtime-tests
> antlr-tgen --config tests/fixtures/config.json

'antlr-tgen' is not recognized as an internal or external command,
operable program or batch file.
03/05-05:24:56 ~/test/mike/antlr4ng
$

@kaby76
Copy link
Contributor

kaby76 commented Mar 5, 2024

I'm now able to reproduce the claimed speed-up for Antlr4ng. Apparently, one should not use version 16.x of NodeJS. You must use at least version 20.x in order to get the speed up. That was the only thing I changed, and afterwards, I see Antlr4ng now works much faster as claimed.

$ bash run.sh ../examples/bitrix_queries_cut.sql ../examples/bitrix_queries_cut.sql
TypeScript 0 ../examples/bitrix_queries_cut.sql success 155.915
TypeScript 1 ../examples/bitrix_queries_cut.sql success 146.365
Total Time: 302.294
03/05-05:51:49 ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-TypeScript
$ pushd
~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-Antlr4ng ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-TypeScript
03/05-06:01:09 ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-Antlr4ng
$ bash run.sh ../examples/bitrix_queries_cut.sql ../examples/bitrix_queries_cut.sql
TypeScript 0 ../examples/bitrix_queries_cut.sql success 74.543
TypeScript 1 ../examples/bitrix_queries_cut.sql success 49.375
Total Time: 123.935
03/05-06:03:28 ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-Antlr4ng
$ node --version
v20.11.1
03/05-06:03:36 ~/test/grammars-v4/sql/mysql/Positive-Technologies/Generated-Antlr4ng
$

@kaby76
Copy link
Contributor

kaby76 commented Mar 5, 2024

I don't know how to test this PR because antlr4 is in an npm package which the package.json references. I don't know how to replace the package reference with a copy of the JavaScript/TypeScript source tree. I get Cannot find module 'antlr4' or its corresponding type declarations.. And, I cannot recreate the package because there are no instructions here. Apparently, the "special sauce" to build is in the package.json file.

$ npm run build

> antlr4@4.13.1 build
> webpack

'webpack' is not recognized as an internal or external command,
operable program or batch file.
03/05-07:12:35 ~/test/after/runtime/JavaScript

In addition, there are probably other changes that should be considered. mike-lischke/antlr4ng@c5c8bea

@jimidle
Copy link
Collaborator

jimidle commented Mar 5, 2024 via email

@ericvergnaud
Copy link
Contributor Author

ericvergnaud commented Mar 5, 2024 via email

@kaby76
Copy link
Contributor

kaby76 commented Mar 6, 2024

I tried out the current 4.13.1 CSharp and TypeScript targets along with the Antlr4ng TypeScript target on the java/java grammar, which is one of our better grammars. I ran the parser on grouped parse mode with the 14 input files. The Antlr4ng is on par with the CSharp target. Yes, Antlr4ng is pretty good.

I haven't yet tested this PR, and I haven't figured out why Antlr4ng is so much better, and why with Node 20.

java

(Ran 10 times, bar graph computed using Octave, errors are 1 SD; test.sh.txt)

@ericvergnaud
Copy link
Contributor Author

ericvergnaud commented Mar 6, 2024 via email

@kaby76
Copy link
Contributor

kaby76 commented Mar 6, 2024

The test.sh is what I wrote to collect and print the results. But there are dependencies. Trash, dotnet, octave, etc.

@kaby76
Copy link
Contributor

kaby76 commented Mar 7, 2024

The Antlr4ng code actually contains a number of optimizations which look significant.

Since Antlr4ng and Antlr 4.13.1 TypeScript are so different in implementation, I wanted to check parser tracing and parser atn tracing. Unfortunately, The TypeScript 4.13.1 API does not define setTrace(), and the Antlr4ng does not define the parser atn tracing flag. In fact, Mike removed all that incredibly valuable code for tracing. (People might not use it, but I do, and it really works in sorting out bugs in the API.) So, I cannot directly compare ATN config sets between CSharp and Antlr4ng, nor parser tracing between CSharp and TypeScript. But, between the stuff I can compare, the parsers across CSharp, TypeScript 4.13.1, and Antlr4ng work identically. I just wish both setTrace() and trace_atn_sim were available everywhere.

@kaby76
Copy link
Contributor

kaby76 commented Mar 7, 2024

I finally figured out how to test this PR. It does not result in any improvement for TypeScript runtime.

I have a drop-in replacement of BitSet.js adapted from Antlr4ng. It does improve on performance, but it is only ~5%.

@ericvergnaud
Copy link
Contributor Author

ericvergnaud commented Mar 8, 2024

I finally figured out how to test this PR. It does not result in any improvement for TypeScript runtime.

I have a drop-in replacement of BitSet.js adapted from Antlr4ng. It does improve on performance, but it is only ~5%.

5% is enormous! I'm closing this PR. Do you want to submit a PR with BitSet.js ? (It's missing unit tests and newbitCount would gain providing references to the algorithm)

@kaby76
Copy link
Contributor

kaby76 commented Mar 8, 2024

If 5% is good, then you'll like the 20% increase with this drop-in replacement for HashSet.js (with MurmurHash.js as a required dependency). I'm thinking HashMap will see an additional performance increase.

I'm thinking why Antlr4? Why not put these changes in Antlr5 alone?

@ericvergnaud
Copy link
Contributor Author

If 5% is good, then you'll like the 20% increase with this drop-in replacement for HashSet.js (with MurmurHash.js as a required dependency). I'm thinking HashMap will see an additional performance increase.

I'm thinking why Antlr4? Why not put these changes in Antlr5 alone?

Because antlr5 uses Kotlin to generate wasm. These optimisations don't make sense there.

@ericvergnaud
Copy link
Contributor Author

Btw I'm using your BitSet.js code but sticking to the old api for now. I'd rather refactor than add an indirection.

@ericvergnaud
Copy link
Contributor Author

If 5% is good, then you'll like the 20% increase with this drop-in replacement for HashSet.js (with MurmurHash.js as a required dependency). I'm thinking HashMap will see an additional performance increase.

Yes HashSet is using an object for key/value mappings, not great!
HashMap has the same issue.
(this code was written before Set and Map were introduced in JS, but they wouldn't be usable here anyway)
FYI HashCode.js IS MurmurHash

@kaby76
Copy link
Contributor

kaby76 commented Mar 8, 2024

Because antlr5 uses Kotlin to generate wasm. These optimisations don't make sense there.

Shouldn't the JavaScript/TypeScript templates and runtime be removed in Antlr5? And, does it make sense to modify the Antlr4 code any further, vis-a-vis limited resources to develop both Antlr4 and Antlr5, and Mike's work on Antlr4ng?

@ericvergnaud
Copy link
Contributor Author

We're keeping the TS template as a reference until we have something kinda working in wasm. Then it will be removed.
A perf improvement is always welcome, especially when it's localized like it is here. We don't want users to switch to antlrng for wrong reasons...

@ericvergnaud
Copy link
Contributor Author

ericvergnaud commented Mar 8, 2024

@kaby76 closing this in favor of #4549 , #4550 and #4551
I've put you as reviewer for these 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants