Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the initial capacity of Go slices vary with type int32 vs int64 when using append?

I've noticed an unexpected behavior when appending elements to slices of different integer types (int32 and int64) in Go. Specifically, the initial capacities assigned to these slices appear to differ based on the type, despite appending the same number of elements.

For instance, when I initialize a slice and append an element using the variadic append function, the capacities differ:

s1 := []int64{}
s2 := []int32{}
s1 = append(s1, []int64{0}...) // len:1, cap:1
s2 = append(s2, []int32{0}...) // len:1, cap:2

And similarly, when appending three elements:

s1 = append(s1, []int64{1, 2, 3}...) // len:3, cap:3
s2 = append(s2, []int32{1, 2, 3}...) // len:3, cap:4

However, when I append elements incrementally, both slices exhibit the same growth pattern:

s1 := []int64{}
s2 := []int32{}
s1 = append(s1, 1) // len:1, cap:1
s1 = append(s1, 2) // len:2, cap:2
s1 = append(s1, 3) // len:3 cap:4

s2 = append(s2, 1) // len:1, cap:2
s2 = append(s2, 2) // len:2, cap:2
s2 = append(s2, 3) // len:3 cap:4

Can someone explain why Go's append function assigns different capacities to slices of different types (int32 vs. int64) when elements are appended in groups, but not when appended one at a time? Is this behavior documented somewhere, or is it an implementation detail of the runtime?

like image 813
Андрей Ходько Avatar asked Oct 16 '25 00:10

Андрей Ходько


1 Answers

As has been mentioned in comments, this will relate to memory alignment and the word size of the architecture you are using.

On a 64-bit platform the CPU registers will be 64 bits (or 8-bytes) in size. The standard int type will be 64 bits, as will pointers, and 64-bit floats are probably going to be the most commonly used too.

That's an awful lot of data moving around in 8-byte chunks, so all manner of aspects of the architecture may be optimised for working with 8 byte chunks of memory. You should see a similar pattern with int8 slices jumping from 0 to 8 capacity, and int16 slices jumping from 0 to 4.

It's worth considering that the underlying array data is heap allocated, and if we allocate 4 bytes of memory for a growing []int32, then there is a 4-byte space which is not correctly aligned for those 8-byte integers, pointers, and floats, so the heap manager will leave that gap when allocating space as we dump more of them onto the heap. Those extra 4 bytes aren't likely to be especially useful at best, and at worst are going to contribute towards heap fragmentation.

like image 131
Joey Sabey Avatar answered Oct 18 '25 06:10

Joey Sabey