Source code analysis of Golang slice expansion mechanism

  table of Contents

One, source code

2. Principle

2.1 Example 1

2.2 Example 2

2.3 Example 3


We know that Golang slices will expand when the capacity is insufficient. What is the principle of expansion? Does it double every time? Below we combine the source code to tell you the answer.

One, source code

Version : go1.15.6  src/runtime/slice.go

//go1.15.6 源码 src/runtime/slice.go
func growslice(et *_type, old slice, cap int) slice {
	//省略部分判断代码
    //计算扩容部分
    //其中,cap : 所需容量,newcap : 最终申请容量
	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
			}
		}
	}

	//省略部分判断代码
}

2. Principle

1. If the current required capacity (cap) is greater than twice the original capacity (doublecap), the final application capacity (newcap) will be the current required capacity (cap);

2. If <condition 1> is not met, it means that the current required capacity (cap) is not more than twice the original capacity (doublecap), then the following judgments will be made;

3. If the original slice length (old.len) is less than 1024, the final application capacity (newcap) is equal to twice the original capacity (doublecap);

4. Otherwise, the final application capacity (newcap, the initial value is equal to old.cap) is increased by newcap/4 each time until it is greater than the required capacity (cap), and then it is judged whether the final application capacity (newcap) overflows, if it overflows, finally The application capacity (newcap) is equal to the required capacity (cap);

In this way, you may not understand, here are a few examples:

2.1 Example 1

Verification condition 1:

package main

import "fmt"

func main() {
	//第1条中的例子:
	var slice = []int{1, 2, 3}
	var slice1 = []int{4, 5, 6, 7, 8, 9, 10, 11, 12}
	fmt.Printf("slice %v len = %v cap = %v\n", slice, len(slice), cap(slice))
	fmt.Printf("slice1 %v len = %v cap = %v\n", slice1, len(slice1), cap(slice1))
	slice = append(slice, slice1...)
	fmt.Printf("slice %v len = %v cap = %v\n", slice, len(slice), cap(slice))
}

Output:

[root@localhost test]# go run main.go 
slice [1 2 3] len = 3 cap = 3
slice1 [4 5 6 7 8 9 10 11 12] len = 9 cap = 9
slice [1 2 3 4 5 6 7 8 9 10 11 12] len = 12 cap = 12
[root@localhost test]#

In Example 1, the required capacity cap = 9+3 = 12, doublecap = 2 * 3 = 6 times the original capacity, and <condition 1> is met, that is: the required capacity is greater than twice the original capacity, so the final application capacity newcap = cap = 12.

2.2 Example 2

Verification conditions 2,3:

package main
import "fmt"

func main() {
	//第2、3条中的例子:
	var slice = []int{1, 2, 3, 4, 5, 6, 7}
	var slice1 = []int{8, 9}
	fmt.Printf("slice %v len = %v cap = %v\n", slice, len(slice), cap(slice))
	fmt.Printf("slice1 %v len = %v cap = %v\n", slice1, len(slice1), cap(slice1))
	slice = append(slice, slice1...)
	fmt.Printf("slice %v len = %v cap = %v\n", slice, len(slice), cap(slice))
}

 Output:

[root@localhost test]# go run main.go 
slice [1 2 3 4 5 6 7] len = 7 cap = 7
slice1 [8 9] len = 2 cap = 2
slice [1 2 3 4 5 6 7 8 9] len = 9 cap = 14
[root@localhost test]# 

In Example 2, the required capacity cap = 7+2 = 9, twice the original capacity doublecap = 2*7 = 14, the original slice length old.len = 7, meets <condition 2, 3>, that is: Required The capacity is less than twice the original capacity, and the original slice length old.len is less than 1024, so the final application capacity newcap = doublecap = 14.

2.3 Example 3

Verification condition 4:

package main
import "fmt"

func main() {
	//第2条中的例子:
	var slice []int
	for i := 0; i < 1024; i++ {
		slice = append(slice, i)
	}
	var slice1 = []int{1024, 1025}
	fmt.Printf("slice %v len = %v cap = %v\n", slice, len(slice), cap(slice))
	fmt.Printf("slice1 %v len = %v cap = %v\n", slice1, len(slice1), cap(slice1))
	slice = append(slice, slice1...)
	fmt.Printf("slice %v len = %v cap = %v\n", slice, len(slice), cap(slice))
}

Output:

[root@localhost test]# go run main.go 
slice [0 1 2 3 4 5 6……1017 1018 1019 1020 1021 1022 1023] len = 1024 cap = 1024
slice1 [1024 1025] len = 2 cap = 2
slice [0 1 2 3 4 5 6……1017 1018 1019 1020 1021 1022 1023 1024 1025] len = 1026 cap = 1280
[root@localhost test]#

In Example 3, the required capacity cap = 1024+2 = 1026, doublecap = 2048, old.len = 1024, and <condition 4> is satisfied, so newcap = 1024 + 1024/4 = 1280.

 

Guess you like

Origin blog.csdn.net/u011074149/article/details/111425091