Sure, here's a Rust/WASM procedural skybox generator I threw together the other day, and is much, much faster at 16k renders then Javascript. https://tkte.ch/night-sky/
1. creating plugins that get executed in the browser to render files like Parquet, PSD, TIFF, SQLite, EPS, ZIP, TGZ, GIS related files and many more, where C libraries are almost always the reference implementations. There are almost a hundred supported file formats, most of which are supported through WASM
2. creating plugins that get executed in the server to generate your own endpoint or middleware while being sure you can't start exfiltrating data (which can be other people's files, and other sensitive stuff)
3. in the workflow engine to enable people to run their own sandboxed scripts without giving those a blank check to go crazy
This is like saying "HTML, CSS and JavaScript are all widely used, but the webcam capture API is used way less, so obviously it's a failure"
In its current scope, WASM is a way to port existing code or accelerate certain computations, which only some applications need. Most websites don't need it, like how most sites don't need to use webcam capture; that doesn't mean it's not useful for those that do
It’s funny how there is continuous reinvention of parsing approaches.
Why isn’t there already some parser generator with vector instructions, pgo, low stack usage. Just endless rewrites of recursive descent with caching optimizations sprinkled when needed.
Because you have to learn how to use any given parser generator, naive code is easy to write, and there are tons of applications for parsing that aren't really performance critical.
Steps 1 and 3 are heavily dependent on the data types that make the most sense for the previous (lexing) and next (semantic analysis) phases of the compiler. There is no one Token type that works for every language, nor one AST type.
The recognizing the grammar part is relatively easy, but since so much of the code is consuming and producing datatypes that are unique to a given implementation, it's hard to have very high performance reusable libraries.
So it went from parsing at 25MiB/s to 115MiB/s. I feel like 115MiB/s is very slow for a Rust program, I wonder what it's up to that makes it so slow now. No diss to the author, good speedup, and it might be good enough for them.
115 MiB/s is something like 20 to 30 cycles per byte on a laptop, 50 on a desktop. That’s definitely quite slow as far as a CPU’s capacity to ingest bytes, but unfortunately about as fast as it gets for scalar (machine) code that does meaningful work per byte. There may be another factor of 2 or 3 to be had somewhere, or there may not be. If you want to go meaningfully faster, as in at least at the speed of your disk[1], you need to stop doing work per byte and start vectorizing. For parsers, that is possible but hard.
The performance gain from using a single shared vector for the nodes is pretty crazy. It just goes to show how much allocation overhead can slow things down if you are not careful.
We (the rust-analyzer team) have been aware of the slowness in Rowan for a while, but other things always took priority. Beyond allocation, Rowan is structured internally as a doubly-linked list to support mutating trees, but:
1. Mutation isn’t really worth it; the API isn’t user-friendly.
2. In most cases, it’s straight up faster to create a new parse tree and replace the existing one. Cache effects of a linked list vs. an arena!
In fairness, I don’t think we predicted just how large L1/L2 caches would get over the coming years.
It seems to me like parser combinators are always more trouble than they're worth. People often have the impression that parsing is difficult and should be outsourced to another library, but often it's pretty simple to hand-roll and usually it makes faster code.
> Use hand-written parser
>
> The old parser was written with winnow which is a parser combinator library.
While it’s easy to create a parser with parser combinators, it’s generally slower than a hand-written parser,
so the first step is to write the parser by hands. Hand-written parser is not only faster but also allows to do more optimizations in the future.
Maintainer of Winnow here. I wish there were more details on this. I switched `toml` / `toml_edit` to being hand written and got some performance boost but I feel like the other things listed would have dwarfed the gains that I got. I wonder if there were sub optimal patterns they employeed that we could find ways to help better guide people.
For anyone going on about "hand written is always better", I disagree. Parser combinators offer a great way to map things back to grammar definitions which makes them much easier to maintainer. Only in extreme circumstances of features and/or performance does it seem worth going hand-written to me.
15 comments
[ 4.1 ms ] story [ 31.4 ms ] threadJust about nobody uses WebAssembly. It first appeared almost ten years ago. This is snail-speed evolution at best.
1. creating plugins that get executed in the browser to render files like Parquet, PSD, TIFF, SQLite, EPS, ZIP, TGZ, GIS related files and many more, where C libraries are almost always the reference implementations. There are almost a hundred supported file formats, most of which are supported through WASM
2. creating plugins that get executed in the server to generate your own endpoint or middleware while being sure you can't start exfiltrating data (which can be other people's files, and other sensitive stuff)
3. in the workflow engine to enable people to run their own sandboxed scripts without giving those a blank check to go crazy
In its current scope, WASM is a way to port existing code or accelerate certain computations, which only some applications need. Most websites don't need it, like how most sites don't need to use webcam capture; that doesn't mean it's not useful for those that do
Why isn’t there already some parser generator with vector instructions, pgo, low stack usage. Just endless rewrites of recursive descent with caching optimizations sprinkled when needed.
1. Consuming tokens.
2. Recognizing the grammar.
3. Producing AST nodes.
Steps 1 and 3 are heavily dependent on the data types that make the most sense for the previous (lexing) and next (semantic analysis) phases of the compiler. There is no one Token type that works for every language, nor one AST type.
The recognizing the grammar part is relatively easy, but since so much of the code is consuming and producing datatypes that are unique to a given implementation, it's hard to have very high performance reusable libraries.
[1] https://www.youtube.com/watch?v=p6X8BGSrR9w
In fairness, I don’t think we predicted just how large L1/L2 caches would get over the coming years.
Maintainer of Winnow here. I wish there were more details on this. I switched `toml` / `toml_edit` to being hand written and got some performance boost but I feel like the other things listed would have dwarfed the gains that I got. I wonder if there were sub optimal patterns they employeed that we could find ways to help better guide people.
For anyone going on about "hand written is always better", I disagree. Parser combinators offer a great way to map things back to grammar definitions which makes them much easier to maintainer. Only in extreme circumstances of features and/or performance does it seem worth going hand-written to me.