New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/compile: inlining confuses escape analysis with interface{} params #53465
Comments
This will be a hard one to fix. The conversion to interface happens when building the Similar to the code below, but for a
The compiler has a |
Could this be fixed as a special case? Recognize exactly these cases: func f {
if safeexpr {
// …
}
} func f {
if !safeexpr {
return
}
// …
} These are very common wrappers for a very common problem with significant readability impact; it might be worth the code weight in the compiler. |
Intuitively, I'm hinting at @josharian's suggestion: as a programmer I'm inclined to move the conditional from the function body to each call site, because there are clearly some gains to be had. But that sort of manual partial inlining is what the compiler should be able to do, not me :)
That does sound like a more generic solution, but I'm not in a position to judge how difficult it is compared to Josh's suggestion. |
Fyi, Zerolog fixed this by not having interface{} parameters in the first place. It does mean the api has to support all types with separate methods. https://github.com/rs/zerolog |
@beoran that is indeed a possible solution, but a drastically different one that I'm not considering at the moment. It is more verbose too, so it doesn't exactly win against Regardless, I think it would be best for the Go compiler to gain this optimization, because |
Here's my inserting of the |
I think this is the same as I see in github.com/go-log/logr (xref #56873). I am seeing people adopt unnatural caller-side checking which feels like what mvdan said (#53465 (comment))
|
Should the basic problem get addressed, would it also eliminate function calls that are needed to construct the parameter slice? In Kubernetes, we often have:
where |
Take a look at https://go.dev/play/p/mPqA0RaybuQ.
On
go version devel go1.19-9068c6844d Thu Jun 16 21:58:40 2022 +0000 linux/amd64
I get the following benchmark results:First, note that both debugf functions are inlined:
And, in the
true
sub-benchmarks where debug logging is enabled and we actually callSprintf
, we can see three allocations - two corresponding to the twointerface{}
parameters, as thestring
andint
variables escape to the heap via the interface, and one for the actual call toSprintf
constructing a string.So far so good. However, when debug logging is disabled, I want to avoid those allocations, because this debug logging happens in a tight loop that runs millions of times in a program, so I want to avoid millions of allocations made in a very short amount of time.
This works fine for the "manually inlined" case where I wrap the call to Debugf with a conditional. Note the zero allocations on
InlinedDebugf/false
. However, that means that every time I want to use Debugf (a dozen times in said program), I need three lines rather than one, and it's pretty repetitive and verbose.One good alternative is to move the conditional inside the Debugf function - and since it's still inlined, it should be the same result. However,
ConditionalDebugf/false
allocates twice, presumably because the two arguments do still allocate, as if I was actually callingSprintf
when in fact I am not.It seems to me like the two benchmarks should behave the same; the compiler's escape analysis and inliner should work well enough together to detect this scenario.
Note that my real use case involves
log.Printf
, which is a no-op when one has calledlog.SetOutput(io.Discard)
. The benchmark above is a simplification which still reproduces the same problem.The text was updated successfully, but these errors were encountered: