Hacker News new | past | comments | ask | show | jobs | submit login

> A recent improvement was in version 1.17, which passes function arguments and results in registers instead of on the stack. This was a significant improvement for GoAWK’s old tree-walking interpreter, where I saw a 38% increase in speed on one microbenchmark, and a 17% average speedup across all microbenchmarks

I find that surprising that Go did not use registers for its (internal) calling convention until 1.17. And I also find it surprising that they only expected a 5% boost from it.




Since its initial release, Go has used a stack-based calling convention based on the Plan 9 ABI, in which arguments and result values are passed via memory on the stack. This has significant simplicity benefits: the rules of the calling convention are simple and build on existing struct layout rules; all platforms can use essentially the same conventions, leading to shared, portable compiler and runtime code; and call frames have an obvious first-class representation, which simplifies the implementation of the go and defer statements and reflection calls. Furthermore, the current Go ABI has no callee-save registers, meaning that no register contents live across a function call (any live state in a function must be flushed to the stack before a call). This simplifies stack tracing for garbage collection and stack growth and stack unwinding during panic recovery.

https://go.googlesource.com/proposal/+/refs/changes/78/24817...


I didn't consider the effects of goroutines, but it's pretty common that JITs that have to emit precise stackmaps simply don't use callee-saved registers, so it doesn't seem any harder to make goroutine stacks first-class. But the detail is in the details, always.


This is basically argument from implementation simplicity. I am of the opinion that implementation simplicity is overrated, and should be essentially ignored, especially for projects as widely used as Go.


It's not. It's in a document proposing a more complex approach.

The point of that discussion, which is clear in the original text, is to identify the benefits of the existing approach and retain them if possible through the transition to a register based ABI.


The cost/benefit of simplicity vs. register calling conventions is probably a part of the reason they switched to register calling conventions.


> I find that surprising that Go did not use registers for its (internal) calling convention until 1.17. And I also find it surprising that they only expected a 5% boost from it.

It's because there's no difference for a normal non-leaf function between a stack calling convention and a register calling convention. If 'f' is a non-leaf function either the caller of 'f' will have to put f's arguments onto the stack (stack calling convention) or 'f' will have to save its argument registers onto the stack before doing its calls (register calling convention).

Register calling conventions end up being a big win if you have lots of non-leaf functions that can forward their arguments (rare) or lots of leaf functions that can't be inlined (also rare). Some programs will get a big boost out of it but most will only get a small one.


We published a 5% win, but it was really closer to 10% (for amd64). We were conservative in our marketing. The arm64 register ABI (which will be in 1.18 [March]), is showing performance improvements of over ≥ 15. (Rob Pike saw 20% on some of his stuff.)


Are you on the go team?




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: