Анализ исходного кода механизма расширения слайсов Golang

  оглавление

Один, исходный код

2. Принцип

2.1 Пример 1

2.2 Пример 2

2.3 Пример 3


Мы знаем, что сегменты Golang будут расширяться, когда мощности недостаточно. Каков принцип расширения? Он каждый раз удваивается? Ниже мы объединяем исходный код, чтобы дать вам ответ.

Один, исходный код

Версия: 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. Принцип

1. Если текущая требуемая емкость (cap) больше чем вдвое исходная емкость (doublecap), конечная емкость приложения (newcap) будет текущей требуемой емкостью (cap);

2. Если <условие 1> не выполняется, это означает, что текущая требуемая мощность (ограничение) не более чем в два раза превышает первоначальную вместимость (doublecap), тогда будут приняты следующие решения;

3. Если исходная длина фрагмента (old.len) меньше 1024, конечная емкость приложения (newcap) равна удвоенной исходной емкости (doublecap);

4. В противном случае конечная емкость приложения (newcap, начальное значение равно old.cap) увеличивается на newcap / 4 каждый раз до тех пор, пока она не станет больше требуемой емкости (cap), а затем проверяется, соответствует ли конечное приложение емкость (newcap) переполняется, если она переполняется, наконец, емкость приложения (newcap) равна требуемой емкости (cap);

Таким образом, вы можете не понять, вот несколько примеров:

2.1 Пример 1

Условие проверки 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))
}

Вывод:

[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]#

В Примере 1 требуемый предел емкости = 9 + 3 = 12, doublecap = 2 * 3 = 6 раз больше исходной емкости и выполняется <условие 1>, то есть: требуемая емкость больше, чем в два раза исходной емкости, поэтому итоговая емкость приложения newcap = cap = 12.

2.2 Пример 2

Условия проверки 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))
}

 Вывод:

[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]# 

В Примере 2 требуемая емкость cap = 7 + 2 = 9, удвоенная исходная емкость doublecap = 2 * 7 = 14, исходная длина фрагмента old.len = 7, соответствует <условию 2, 3>, то есть: Требуется емкость меньше чем в два раза исходная емкость, а исходная длина фрагмента old.len меньше 1024, поэтому конечная емкость приложения newcap = doublecap = 14.

2.3 Пример 3

Условие проверки 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))
}

Вывод:

[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]#

В примере 3 необходимое ограничение емкости = 1024 + 2 = 1026, doublecap = 2048, old.len = 1024 и <условие 4> выполнено, поэтому newcap = 1024 + 1024/4 = 1280.

 

рекомендация

отblog.csdn.net/u011074149/article/details/111425091