3 ms·
How common is it for stack frames to be deeper than 32 calls, where these techniques cross in the benchmark? I don't program in Go, but it doesn't seem that com
by naasking 2y ago
How common is it for stack frames to be deeper than 32 calls, where these techniques cross in the benchmark? I don't program in Go, but it doesn't seem that common in the languages I've used, so I'm skeptical it's worth it.
- malkia 2y agoOn Windows, I tend to often see callstack frames going down a lot, especially in C++ debug builds where inlines are no longer such. On top of that if you have work-stealing task scheduler, that if your current thread waits on I/O reassigns it to some other task to work it may bulb even more.
- naasking 2y agoCan you clarify what you mean by "going down a lot"? I interpret that to mean that the stacks are flatter, but then debug builds without inlining would grow the stack, not flatten it, so I'm not clear on what you're saying. Scheduling tasks on a work-stealing queue tends to flatten the stack first, eg. a halted task has a nearly empty stack if it's an async coroutine. The scheduler itself may have 3-4 calls deep before resuming the task itself, so that doesn't seem too bad. Unless you're talking about full blown threads.
- malkia 2y agoSorry I meant to say is that inlines are now individual functions, so the amount of callstack entry items is going to grow. But then, if you capture the stack (using sampling), and catch for example ThreadContext switch while CreateFileA is happening, you can expect lots of callstack items going all the way down to the kernel
- felixge 2y agoI'm skeptical that it's worth it myself, this was just a fun research project for me. But once hardware shadow stacks are available, I think this could be great. To answer your first question: For most Go applications, the average stack trace depth for profiling/execution tracing is below 32 frames. But some applications use heavy middleware layers that can push the average above this limit. That being said, I think this technique will amortize much earlier when the fixed cost per frame walk is higher, e.g. when using DWARF or gopclntab unwinding. For Go that doesn't really matter while the compiler emits frame pointers. But it's always good to have options when it comes to evolving the compiler and runtime ...