Go切片扩容
以下的内容为Go1.20.2版本的扩容方式
Go数组的扩容机制简单来说分为两种情况
-
一种是原来的容量小于256,此时会将原来的容量翻倍作为新的容量
-
一种是容量大于等于256,会按照以下方式扩容直至可以满足需求
这是大致的分配方式,实际上情况还要更复杂一些,接下来通过切片扩容的代码来分析一些细节
1 | newcap := oldCap |
暂时得到的容量结果
情况 | newcap | |
---|---|---|
容量翻倍后不能满足需求 | newcap = newLen | |
容量翻倍可以满足需求,原容量小于256 | newcap = 2 * oldcap | |
容量翻倍可以满足需求,原容量大于等于256 | newcap += (newcap + 3*256) / 4直至大于newLen |
我们暂时得到了一个newcap,但实际上最后扩容的过程中还要对这个newcap做一步最后的处理
1 | lenmem = uintptr(oldLen) * et.size |
核心在这个roundupsize函数,至于下面的魔法数组如何得到就不深入探究了
1 | var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 24, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768} |
我们来看一段示例代码说明这个函数是如何工作的
1 | package main |
切片 | len | cap |
---|---|---|
s | 2 | 2 |
现在我们将容量翻倍得到4,但是现在需求的长度是5,所以翻倍不能满足要求,最后newcap直接更新为5。
这个数组要40个字节的空间,传入参数size为40,然后我们计算通过roundupsize得到最终结果
处理过程 | 结果 |
---|---|
divRoundUp(size, smallSizeDiv) | 40 / 8 = 5 |
size_to_class8[5] | 5 |
class_to_size[5] | 48 |
我们可以分析出最后得到的cap空间大小是48字节,也就是cap为6,所以扩容的结果最后为6
我们得到整个扩容过程
- 看看翻倍原容量能否满足需求
- 不能,则先得到初步容量为需求的长度
- 能
- 原容量小于256,初步容量为原容量翻倍
- 原容量大于等于256,按照公式扩容直至满足需求
- 最后将容量roundupsize
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 jking の 博客!