It looks like, overall, this design gets the parser about twice as fast as a simple one that creates tree-like ASTs. That's not nothing. But a parser is rarely the most time-intensive part of a production compiler. And the parser does get iterated on a lot in languages that are evolving and adding new syntax. Given that, I'd be inclined to take the performance hit and stick with a simpler AST representation if that y…
Super-Flat ASTs
11–20 of 27 posts
Re: Super-Flat ASTs
#12E.g., https://github.com/llvm/llvm-project/blob/62e00a03fba029f82d...
and
https://github.com/llvm/llvm-project/blob/62e00a03fba029f82d...
Re: Super-Flat ASTs
#13What I like about this writeup is that it surfaces a tension most “let’s build a compiler” tutorials skip: the AST is both a data structure and a UX boundary. Super-flat layouts are fantastic for cache and memory, but they’re hostile to all the things humans care about (debuggable shapes, easy instrumentation, ad-hoc traversals, “just print this node and its children” in a debugger). A lot of production compilers qui…
Re: Super-Flat ASTs
#14I mean if you do an off by one error on indices, essentially you are readin the pointer of another node.
Re: Super-Flat ASTs
#15It looks like, overall, this design gets the parser about twice as fast as a simple one that creates tree-like ASTs. That's not nothing. But a parser is rarely the most time-intensive part of a production compiler. And the parser does get iterated on a lot in languages that are evolving and adding new syntax. Given that, I'd be inclined to take the performance hit and stick with a simpler AST representation if that y…
That's a good caution. However, traversing a flat AST (iterating a "struct of arrays" rather than a pointer-based tree) is also going to be faster. So the next steps of the compiler, say type checking and code emitting, will also be faster. But how much, or whether it's worth it even then, I'm not sure.
Re: Super-Flat ASTs
#16When investigating performance issues its often very helpful to run with profiling instrumentation enabled and start by looking at some top-down "cumulative sum" profiler output to get a big picture view of which functions/phases are consuming most of the running time, to see where it may be worth spending some effort.
Getting familiar with linux's perf [1] tool is also helpful, both in terms of interpreting summary statistics from perf stat (instructions per cycle, page faults, cache misses, etc) that can give clues what to focus on, but also being able to use it to annotate source line by line with time spent.
I'm not familiar with rust, but e.g. the rustc compiler dev guide has a tutorial on how to profile rustc using perf [2]
[1] Brendan Gregg's Linux perf examples is an excellent place to start https://www.brendangregg.com/perf.html [2] https://rustc-dev-guide.rust-lang.org/profiling/with_perf.ht...
Re: Super-Flat ASTs
#17It looks like, overall, this design gets the parser about twice as fast as a simple one that creates tree-like ASTs. That's not nothing. But a parser is rarely the most time-intensive part of a production compiler. And the parser does get iterated on a lot in languages that are evolving and adding new syntax. Given that, I'd be inclined to take the performance hit and stick with a simpler AST representation if that y…
I didn't go into this at all, but the main benefit of this design is how well it interacts with CPU cache. This has almost no effect on the parser, because you're typically just writing the AST, not reading it. I believe that subsequent stages benefit much more from faster traversal.
(By the way, I am a huge fan of your work. Crafting interpreters was my introduction to programming languages!)
Re: Super-Flat ASTs
#18What I like about this writeup is that it surfaces a tension most “let’s build a compiler” tutorials skip: the AST is both a data structure and a UX boundary. Super-flat layouts are fantastic for cache and memory, but they’re hostile to all the things humans care about (debuggable shapes, easy instrumentation, ad-hoc traversals, “just print this node and its children” in a debugger). A lot of production compilers qui…
What about this representation is hostile to humans and ad-hoc traversals? Don't convenience "getters" basically solve usability?
Re: Super-Flat ASTs
#19Earlier quoted context omitted.
What about this representation is hostile to humans and ad-hoc traversals? Don't convenience "getters" basically solve usability?
(author here) If you run the parser under a debugger like lldb, then attempt to inspect the AST of a program, it appears as an array of u64. Not very useful, unless you work on special support for debuggers (such as a python script to unpack it in lldb). Compare that to a tree of pointers, you can "expand" nodes without any extra effort.
Re: Super-Flat ASTs
#20Personally I think this is a neat trick to organize memory, but don't these kinds of objects packed together in flat buffers bypass the entire lifetime and safety mechanism of Rust? I mean if you do an off by one error on indices, essentially you are readin the pointer of another node.