3 ms·
> Allocate capacity in make to avoid re-allocation Am I the only one who has done this and used append() within a loop, and resulted in a slice that is 2x as l
by astockwell 7y ago
> Allocate capacity in make to avoid re-allocation
Am I the only one who has done this and used append() within a loop, and resulted in a slice that is 2x as long as the original desired length, and the first 1x of items are all empty?
I quickly caught it and fixed the approach (instead of append, I used the `i` as the insertion index into my new empty slice which already had capacity allocated), but the ease with which that subtlety could be overlooked turned me off to this approach unless I profile and really find it's a hot path.
Edit to add: I tried his code, and it resulted in the new slice being as expected.
- capo64 7y agoThe make function takes the type, length and capacity. If you don’t supply capacity, it defaults to length. So if you want to use append, do something like: make([]int, 0, length) That will allocate a slice that can fit length ints but has a length of 0 (so append starts at index 0). You can call len(slice) and cap(slice) to see the difference, but append will insert an element at index len(slice), growing the capacity if necessary
- astockwell 7y agoAhh thanks for the great explanation! Love HN.
- ben0x539 7y agoYou're running into the difference between make([]T, 0, n) and make([]T, n)
- weberc2 7y agoI occassionally hit this bug, but it's pretty easy to spot after I introduced it a couple of times. Allocations in Go are slow enough that I usually do this by default unless I know that performance absolutely will never be critical (e.g., doing CLI boilerplate that just needs to be fast on human timescales).
- jerf 7y agomake can take three parameters for slices, which is the type, the initial length, and the initial "capacity": https://play.golang.org/p/8d1mS1mGsW6 https://play.golang.org/p/8d1mS1mGsW6 By default, the slice is filled in and fully initialized. On the one hand, IMHO and IME that's not really what you want if you're going to append things which is a pretty common case, but on the other hand, worrying about "capacity" for slices, while perfectly sensible once you understand the abstraction, is something you can get in trouble with if you're not paying attention, because it makes you really pay attention to the fact the slice has an underlying array. (Many languages have this concept somewhere, Go just happens to put it in the syntax: A "slice" is an object with a reference to an underlying array, an index of the first element, a length, and a "capacity" which can be smaller than the underlying array has room for. If you want to grow past the underlying array, the runtime then has to create a new one and copy the elements out into that.) Similarly, if you have a slice of ten elements and you want to take a subset of it and start appending your own bits and pieces, you have to do that carefully: https://play.golang.org/p/QcZOndSp-9x https://play.golang.org/p/QcZOndSp-9x There's a syntax for reducing the capacity of the new slice deliberately, which forces any new "append" to copy to a new slice, producing the behavior you want. For as tricky as this can get, I'm surprised it doesn't come up more often, but so far in the last five years, this was the source of my bug I believe twice, and one of them I was doing rather inadvisable things anyhow. It turns out that in most "just code" you're not cutting lists up and distributing them various places very often.