These are some experiments attempting to speed up SWC's .parse()
method.
TLDR: One of these experiments is quite successful:
SWC's parser is extremely fast. However, swc.parse()
/ swc.parseSync()
is slow.
There seem to be 2 bottlenecks:
- Cost of passing AST from Rust to JS (as JSON string)
- Cost of parsing JSON in JS (using
JSON.parse()
)
I have tried 3 different experiments.
These are all proof of concept only. Implementations shown here can only handle a very limited subset of JavaScript (a sequence of const x = 1;
statements).
NB Apologies for the poor quality of the Rust code! Before this weekend, I'd never written a line of Rust in my life.
The shape of the JSON is predictable, therefore a lot of the work JSON.parse()
does is redundant.
I wrote a custom JSON parser and substituted it for JSON.parse()
.
This produces approx 30% speed-up.
Instead of outputting a JSON string from Rust, output a binary serialization of the AST as a JsBuffer
.
This has 2 advantages:
JsBuffer
can be created in main thread's memory so passing it from Rust to JS is zero cost.- AST can be encoded more efficiently in binary form.
This produces approx 5x speed-up, especially with longer inputs. It achieves a speed faster than Babel.
Both the Rust and JS code is not at all optimized - with futher work, I imagine speed could be further improved.
See Rust serializer and JS buffer deserializer.
Create AST as JsObject
in Rust, avoiding serialization/deserialization entirely.
Abject failure. Much slower than original SWC.
Benchmarks with different lengths of input JS.
npm install
npm run build
npm test
npm run bench
NB: swc (without deserialization)
and experiment 2 - buffer (without deserialization)
are not complete. The first returns only a JSON string, the 2nd only a buffer representation of AST. I've included these only as it's interesting to see where the time is going.
Note how slow SWC is on larger files even without deserializing the JSON - indicating the high cost of passing a large JSON string from Rust to JS.