教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 Golang 切片容量(cap)增长探秘

Golang 切片容量(cap)增长探秘

发布时间:2022-02-02   编辑:jiaochengji.com
教程集为您提供Golang 切片容量(cap)增长探秘等资源,欢迎您收藏本站,我们将为您提供最新的Golang 切片容量(cap)增长探秘资源
<h1>在go 指南, 复习切片的时候,发现切片容量增长在一定范围内并不固定为2倍。</h1>

 

<h1>以下内容为全文转载,地址</h1>

 

<h1>golangSlice的扩容规则</h1>

一段代码

       以前一直以为go语言中的slice,也就是切片,其容量增长规则与std::vector一样,指数扩容,每次扩容都增长一倍,没有去详细了解过源代码。直到同事丢给了我以下这段代码:

<pre><code> s := []int{1,2} s = append(s,4,5,6) fmt.Printf("%d %d",len(s),cap(s)) </code></pre>

       如果简单地按照指数扩容,那么结果应该是 5,8。从初始化时的 2,2 扩容到 4,4 ,然后增长到 5, 8。但结果并不是这样,而是输出了 5,6。深入测试这段代码,每次往s中append两个元素,往后cap(s)都是6的倍数增长。

       源代码位于<code>runtime/slice.go</code>中,关于slice增长的函数是<code>growslice</code>,其中的代码,决定了slice的扩容规则

基本cap的增长规则

<pre><code> newcap := old.cap if newcap newcap < cap { newcap = cap } else { for { if old.len < 1024 { newcap = newcap } else { newcap = newcap / 4 } if newcap >= cap { break } } } </code></pre>

       此处的cap是旧容量加上新加入元素大小的结果,也就是此处slice扩容的理论上的最小值,old就是旧的slice。可以看到cap增长基本规则是,若新入元素大小通过倍数增长能够hold住,那就根据旧容量是否超过1024决定是倍数增长还是1.25倍逐步增长;若新入元素大小超过了原有的容量,则新容量取两者相加计算出来的最小cap值。

       于是在例子中,经过扩容规则,()2 3 = 5) > (2 *2 = 4 ),newcap应当取5

内存对齐

       计算出了新容量之后,还没有完,出于内存的高效利用考虑,还要进行内存对齐

<pre><code>capmem := roundupsize(uintptr(newcap) * uintptr(et.size)) </code></pre>

       newcap就是前文中计算出的newcap,et.size代表slice中一个元素的大小,capmem计算出来的就是此次扩容需要申请的内存大小。roundupsize函数就是处理内存对齐的函数。

<pre><code>func roundupsize(size uintptr) uintptr { if size < _MaxSmallSize { if size <= 1024-8 { return uintptr(class_to_size[size_to_class8[(size 7)>>3]]) } else { return uintptr(class_to_size[size_to_class128[(size-1024 127)>>7]]) } } if size _PageSize < size { return size } return round(size, _PageSize) } </code></pre>

       _MaxSmallSize的值在64位macos上是32«10,也就是2的15次方,32k。golang事先生成了一个内存对齐表。通过查找(size 7) » 3,也就是需要多少个8字节,然后到class_to_size中寻找最小内存的大小。承接上文的size,应该是40,size_to_class8的内容是这样的:

<pre><code>size_to_class8:1 1 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11... </code></pre>

       查表得到的数字是4,而class_to_size的内容是这样的:

<pre><code>class_to_size:0 8 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 256... </code></pre>

       因此得到最小的对齐内存是48字节。完成内存对齐计算后,重新计算应有的容量,也就是48/8 = 6。扩容得到的容量就是6了。

到此这篇关于“Golang 切片容量(cap)增长探秘”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
Golang 切片容量(cap)增长探秘
Golang make多种使用方法详解
Golang 切片(slice)扩容机制源码剖析
Go语言切片详解
GoLang中的切片扩容机制
【golang】切片扩容原理,完成入栈操作
切片 里面包含interface_Golang数据结构详解之切片
golang slice 最后一个元素_Go 常见的数据结构 Slice
golang面试题
Golang slice 切片原理

[关闭]
~ ~