You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It is widely known that passing anything to an interface method call with arguments that contain pointers causes heap allocations of that value. This happens because the compiler’s escape analysis cannot prove that the call would not cause the values to be escaped. Even though we don't know exactly which function is going to be called, there is one thing that we know at the caller site: the signature of the function being called. Using that fact, the compiler could collect all functions with the same function signature, run escape analysis over them and then use the per-signature result to decide whether dynamic dispatch calls need to have the arguments escaped. You can think of this as a fallback. Currently the fallback is to heap allocate, with this approach, if none of the functions with the same signature cause their arguments to escape, then dynamic dispatch calls would not require heap allocation either.
If this was implemented in the compiler I would expect a reduced heap allocations count in most programs, some examples:
The byte slice passed to io.Reader/io.Writer would likely not need to be allocated on the heap.
Arguments passed to http.Handler to be stack allocated (they are often wrapped, especially the http.ResponseWriter).
Log functions in log/slog would not cause allocations, and the optimization in slog.Record would not be really needed anymore:
Obviously, this would not work in all cases and has some drawbacks, it only takes one function to force heap allocation of other dynamic dispatch calls (of functions with same signature). This might easily prevent signatures based solely on basic types from benefiting from this optimization, say: func(b []byte) { go func ( /* do sth with b */ }, so folks might pollute functions with some unused and unneeded parameters just to make this optimization work: func(b []byte, _ ...struct{}). This optimization would be beneficial for signatures that take/return custom (package-local) types, like log/slogs Handle(context.Context, slog.Record) error.
This is just an idea that came to my mind while reading #62653. I'm not even sure whether this change is rational, because changes in the escape analysis results would most likely require the dependencies to be recompiled, changes to the cache and probably some other stuff that I am not even aware of.
The no-copy string -> []byte optimization could also take benefit from this kind of optimization.
It is widely known that passing anything to an interface method call with arguments that contain pointers causes heap allocations of that value. This happens because the compiler’s escape analysis cannot prove that the call would not cause the values to be escaped. Even though we don't know exactly which function is going to be called, there is one thing that we know at the caller site: the signature of the function being called. Using that fact, the compiler could collect all functions with the same function signature, run escape analysis over them and then use the per-signature result to decide whether dynamic dispatch calls need to have the arguments escaped. You can think of this as a fallback. Currently the fallback is to heap allocate, with this approach, if none of the functions with the same signature cause their arguments to escape, then dynamic dispatch calls would not require heap allocation either.
If this was implemented in the compiler I would expect a reduced heap allocations count in most programs, some examples:
io.Reader/io.Writerwould likely not need to be allocated on the heap.http.Handlerto be stack allocated (they are often wrapped, especially thehttp.ResponseWriter).log/slogwould not cause allocations, and the optimization inslog.Recordwould not be really needed anymore:go/src/log/slog/logger.go
Lines 271 to 272 in 215de81
go/src/log/slog/record.go
Lines 38 to 50 in 215de81
It is also worth noting that
log/slogAPI would likely not need theEnabled()method if this kind of optimization was present:go/src/log/slog/handler.go
Lines 31 to 41 in 215de81
sync.Pool(proposal: net/http: add Request.CopyTo #68501)Obviously, this would not work in all cases and has some drawbacks, it only takes one function to force heap allocation of other dynamic dispatch calls (of functions with same signature). This might easily prevent signatures based solely on basic types from benefiting from this optimization, say:
func(b []byte) { go func ( /* do sth with b */ }, so folks might pollute functions with some unused and unneeded parameters just to make this optimization work:func(b []byte, _ ...struct{}). This optimization would be beneficial for signatures that take/return custom (package-local) types, likelog/slogsHandle(context.Context, slog.Record) error.This is just an idea that came to my mind while reading #62653. I'm not even sure whether this change is rational, because changes in the escape analysis results would most likely require the dependencies to be recompiled, changes to the cache and probably some other stuff that I am not even aware of.
The no-copy string -> []byte optimization could also take benefit from this kind of optimization.
CC @golang/compiler