为什么奇数的切片容量与偶数的行为不同

时间:2015-10-07 14:56:25

标签: go slice

我注意到,当容量为奇数时,切片的容量会以不同的方式运行。更具体地说:当元素添加到切片时,当原始容量为偶数时,切片的容量加倍。但是当原始容量是奇数时,容量增加1然后加倍。例如:

s := make([]int, 28, 28)
s = append(s, 1) 
fmt.Println("len=", len(s), " cap=", cap(s)) // len = len + 1, cap = 2 * cap


pri := make([]int, 27, 27)
pri = append(pri, 1)
fmt.Println("len=", len(pri), " cap=", cap(pri)) // len = len + 1, cap = 2 * (cap + 1)  

假设这不是一个错误,那么这种行为的原因是什么?

指向游乐场的链接:http://play.golang.org/p/wfmdobgCUF

1 个答案:

答案 0 :(得分:18)

简短回答

将切片容量四舍五入以填充分配的内存块。

答案很长

让我们看一下Go1.5.1源代码:

https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/cmd/compile/internal/gc/walk.go#L2895告诉我们RestClient已扩展为

append(l1, l2...)

我们感兴趣的部分s := l1 if n := len(l1) + len(l2) - cap(s); n > 0 { s = growslice_n(s, n) } s = s[:len(l1)+len(l2)] memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) 在那里定义:https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/slice.go#L36

进一步深入,我们发现:

growslice_n

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 } } } /* [...] */ capmem := roundupsize(uintptr(newcap) * uintptr(et.size)) newcap = int(capmem / uintptr(et.size)) 在那里定义:https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/msize.go#L178

roundupsize

它是在那里引入的:https://groups.google.com/forum/#!topic/golang-codereviews/bFGtI4Cpb_M

  

当增长切片时考虑分配的内存块的大小。