Skip to content
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: global vars initialized with func calls like net.IPv4 allocate at init time #51773

Open
mvdan opened this issue Mar 17, 2022 · 4 comments
Labels
NeedsInvestigation Performance

Comments

@mvdan
Copy link
Member

@mvdan mvdan commented Mar 17, 2022

See https://go-review.googlesource.com/c/go/+/391114, which replaces lines like:

var IPv4bcast = IPv4(255, 255, 255, 255) // limited broadcast

with:

var IPv4bcast = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 255, 255, 255, 255} // limited broadcast

The latter is certainly more verbose, which is the reason why the CL isn't submitted, but the former does allocate at init time as reported by GODEBUG=inittrace=1. Below are three samples before and after the 7 var changes:

init net @0.98 ms, 0.016 ms clock, 2840 bytes, 74 allocs
init net @1.0 ms, 0.017 ms clock, 3048 bytes, 75 allocs
init net @0.96 ms, 0.018 ms clock, 3048 bytes, 75 allocs

init net @1.1 ms, 0.020 ms clock, 2760 bytes, 67 allocs
init net @1.6 ms, 0.017 ms clock, 2760 bytes, 67 allocs
init net @0.98 ms, 0.018 ms clock, 2760 bytes, 67 allocs

The funcs in question are tiny, and they allocate fixed-size slices. As far as I can tell, they are getting inlined. I imagine it should be possible for the compiler to avoid the allocations even with the function calls.

cc @randall77 @josharian

@mvdan mvdan added Performance NeedsInvestigation labels Mar 17, 2022
@randall77
Copy link
Contributor

@randall77 randall77 commented Mar 17, 2022

The problem is even if inlined, the compiler at most allocates storage on the stack. Because it is assigning to a global variable, escape is inevitable. The compiler doesn't understand that things in init functions (and not in loops) happen at most once, so allocations in those places can be done statically.

You could force it static. Do:

var IPv4bcastStore [IPv6len]byte
var IPv4bcast = IP(IPv4bcastStore[:])
func init() {
    copy(IPv4bcast, IPv4(255, 255, 255, 255))
}

Then if IPv4 is inlined, its allocation will be on the stack.

More verbose, but maybe a bit less breaking open of abstractions.

@mvdan
Copy link
Member Author

@mvdan mvdan commented Mar 17, 2022

The compiler doesn't understand that things in init functions (and not in loops) happen at most once, so allocations in those places can be done statically.

Is there a plan to ever change that? I imagine such calls to relatively simple funcs are happening in globals in many packages, and I understand that a few init time allocs here and there are not the biggest deal, but they also add up.

Your alternative is interesting, though it seems even more verbose than mine with the literal, so I'm not sure if @ianlancetaylor would approve it :) It does feel like it's too clever for my taste, and I wouldn't want others copying the workaround to avoid a global init alloc.

@randall77
Copy link
Contributor

@randall77 randall77 commented Mar 17, 2022

Is there a plan to ever change that?

There is no plan. But I imagine that it wouldn't be that hard.
The only worry I would have is that global allocations are forever, so if the allocation dies early it can't be reclaimed (like it would be if allocated on the stack or heap).

@mvdan
Copy link
Member Author

@mvdan mvdan commented Mar 17, 2022

Happy to leave this as a backlog item for now, then :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Performance
Projects
None yet
Development

No branches or pull requests

2 participants