In which I reverse engineered "inline pointers".
As you can see from the directory name ("multihoisting"), when I began exploring this topic, I didn't know they were called that. :/ This thus turned out to very much be one of those occasions where knowing the *right two words* to put into a search engine would've saved a fairly enormous number of hours. Once I finally found the right term, some perfectly lovely documentation appeared. But alas. (Like the previous commits, this stuff is coming from a while ago; roughly Oct 25. It uncovers a lot of stuff that gets us much closer to being able to make correct and performant designs to minimize and amortize the number of allocations that will be required to make our node trees work in codegen (though with that said, there will also still be plenty of details in need of refinement after this, too).) Still working on a "HACKME_memorylayout.md" doc, which will appear in the codegen directories in the near future and contain a summary of these learnings and how they balance against other design concerns. Meanwhile, a couple of other notes in their rough form: - basically, don't use non-pointer methods. - turns out value receivers tend to mean "copy it", and pointer receivers *don't* mean "heap alloc it" (they just mean "consider escape, maybe"). - so overall you're tying the compilers hands when you use a value receiver, and you're *not* when you use a pointer receiver. - taking pointers to things already being passed around in pointer form or already having heap-escaped seems to be free. - it might demand something become heap alloc if it wasn't already... - but this turns out to be fairly amortizable. - because if you write nearly-everthing to be pointers, then, there you go. - and if you're lucky and something is shortlived (provably doesn't escape), then even whole stacks of ptr-receiver methods will still probably inline and collapse to no heap action. - the analysis on this seems to reach much further than i thought it would. - `-gcflags '-m -m'` was extremely revealing on this point. - tl;dr: - more pointers not less in all functions and passing; - but do have one struct that's the Place of Residence of the data without pointers. - this pair of choices probably leads to the best outcomes. - hokay so. applied to topic: two sets of embeds. - builders might as well have their own big slab of embed too. - logically nonsense to embed them, but incredibly rare you're not gonna use the memory, so! And a couple important incantations, which can help understand What's Really Going On Here in a bunch of ways: - `-gcflags '-S'` -- gives you assembler dump - `-gcflags '-m'` -- gives you escape analysis (minimally informative and hard to read) - `-gcflags '-m -m'` -- gives you radically more escape analysis, in stack-form (actually useful!!) - `-gcflags '-l'` -- disables inlining! I learned about the '-m -m' thing by grepping the Go compiler source, incidentally. It's a wildly under-documented feature. No joke: I encountered it via doing a `grep "Debug['m']` in go/src; there is currently no mention of it in `go tool compile -d help`. Once I found the magic string, and could submit it to search engines, I started to find a few other blogs which mention it... but I'd seen none of them (and had not found queries that turned them up) until having this critical knowledge already in-hand. >:I So chalking up another score for "the right words would've been nice". Performance work is fun!
Showing
Please register or sign in to comment