# Slice expansion method

Time：2022-5-8

Q: Please introduce yourself briefly first
A: Mani mani mani mani mani mani mani mani mani mani mani Mani, Mani mani mani mani Mani
Q: You talk so much. Do you know arrays?
A: Array is a data structure with the same type and fixed length. Because the length cannot be changed, it is rarely used in practice. It is generally replaced by slicing.
Q: You just talked about slicing. What are the similarities and differences between slicing and array?
A: The same point is that they can only store the same type of data structure, and the underlying data of the slice is also based on the array. The difference is that the length of the slice can be changed and the capacity can be expanded automatically.
Q: Well, how is the bottom layer of the slice composed? Do you know its expansion rules?
A: The slice consists of three parts: the length of the slice, the capacity of the slice and the data of the slice (the pointer to the underlying array). It is difficult to say the expansion rule.
Q: It’s hard to say. Hurry up.
A: The basic rule of slice expansion is: when the slice append (the following function will be called), the capacity of the new slice must be greater than twice the capacity of the old slice, then the capacity of the new slice is the necessary capacity of the new slice. On the contrary, if the length of the old slice is less than 1024, the capacity of the new slice is equal to twice the capacity of the old slice. If the old slice is greater than or equal to 1024, Then the new slice capacity is equal to 1.25 times of the old slice capacity, which is the estimated slice capacity.

``````// runtime/slice.go
func growslice(et *_type, old slice, cap int) slice {
//Et represents an element of slice, old represents the old slice, and cap represents the necessary capacity of the new slice
...
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = cap
}
}
}
...
}``````

According to the above rules, the necessary slice capacity for the next slice expansion is 5, 5 > 2 * 2, so it should be printed out`len=5, cap=5`

``````package main

import (
"fmt"
)

func main() {
s := []int{1, 2}
s = append(s, 4, 5, 6)
fmt.Printf("len=%d, cap=%d", len(s), cap(s))
}``````

But the actual printing is`len=5, cap=6`, this function will be called when memory alignment is carried out after the capacity calculation is completed, taking into account the efficient utilization of memory

``````func roundupsize(size uintptr) uintptr {
if size < _MaxSmallSize {
if size <= smallSizeMax-8 {
return uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]])
} else {
return uintptr(class_to_size[size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv]])
}
}
if size+_PageSize < size {
return size
}
return alignUp(size, _PageSize)
}``````

Size indicates the memory size required for the new slice. The int type we passed in occupies 8 bytes each (can be called)`unsafe.Sizeof()`Function to check the occupied size), there are 5 in total, so it is 40, and the size is less than_ Maxsmallsize and less than smallsizemax-8, then use the general formula`(size+smallSizeDiv-1)/smallSizeDiv`Calculated to be 5,
Then to`size_to_class128`The fifth element found is`4`, and then from`class_to_size`Find that the fourth element is 48, which is the memory size occupied by the new slice. Each int occupies 8 bytes, so the capacity of the final slice is 6. Therefore, the expansion of slices has its basic expansion rules, and memory alignment should be considered after the rules, which means that the expansion capacity of slices of different data types will be inconsistent.
Q: OK, let’s move on to the next question