-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: pre-allocate a slice of provable length #64595
Comments
I implemented this some time ago and this was less interesting that it looks like. If your loop has return statements or branches where the slices is not used (like panics) it is not obvious you want to preallocate the slice. func transform(a []int, f(int) uint) (r []uint) {
for _, x := range a {
r = append(r, f(x))
}
return
} Here Maybe you say, panics are cold let's optimize for cases where they don't happen. func transform(a []int, f(int) (uint, error)) (r []uint, _ error) {
for _, x := range a {
v, err := f(x)
if err != nil { return nil, err }
r = append(r, )
}
return
} Do you also want to say that returning non Given theses conservative considerations this matched few cases, |
There is also a linter which helps with this https://github.com/alexkohler/prealloc, it's not good enough to do machine fixes, but it's way good enough for primates look at it's output and fix interesting cases. |
Thanks for the link. I've checked our code base and most common matches are those that transform one slice into another. Should we consider adding to func AppendFunc[T ~[]R, E, R any](t T, mapper func(element E) R, s ...E) T {
newslice := Grow(t, len(s))
for _, v := range s {
newslice = append(newslice, mapper(v))
}
return newslice
}
func MapFunc[R any, T []R, E any](mapper func(element E) R, s ...E) T {
return AppendFunc([]R(nil), mapper, s...)
} to cover trivial but common cases? |
@randall77 Thank you. Those revolve around iter.Seq which does not provide size so they will not be able to pre-allocate result afaict. |
True. The size could be passed somehow using an unexported method, although I don't immediately see an obvious way to do that (we could return a named |
The issue at hand is that I generally don't want to think about it when writing code. |
Several of us have recently been brainstorming online slice/map pre-sizing (#64703). To a degree, this is a special case of that, where the ideal [1] size can be determined statically. [1] The exception is that |
I wonder how often this optimization makes things slower: If |
Go version
go1.21.2
What did you do?
Compile the following snippet:
What did you expect to see?
I would expect it to compile to something like:
I expect to see the following:
makeslice
of a given length and capacity. The elements within the length need not be explicitly zeroed (since it can be provably overwritten).append
call in the loop be essentially rewritten as an index operation, thus avoiding a call toruntime.growslice
in every iteration of the loop.The conversation of
int
touint
is for demonstration, but the above optimization should be doable for any conversion that does not panic.What did you see instead?
Right now, this compiles more or less to what you see in the original code snippet.
The text was updated successfully, but these errors were encountered: