英文:
Why does slice capacity with odd numbers differ from behavior with even numbers
问题
我注意到,当切片的容量是奇数时,切片的容量表现出不同的方式。具体来说:当向切片添加元素时,如果原始容量是偶数,切片的容量会加倍。但是,当原始容量是奇数时,容量会增加一倍然后再加一。例如:
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)
假设这不是一个 bug,这种行为的原因是什么?
Playground 链接:http://play.golang.org/p/wfmdobgCUF
英文:
I noticed that the capacity of slices behaves in a different way, when the capacity is an odd number. More specifically: When an element is added to a slice, the capacity of the slice is doubled when the original capacity was an even number. But when the original capacity was an odd number, the capacity is incremented by one and then doubled. Example:
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)
Assuming this is not a bug, what's the reason for this behavior?
Link to playground: http://play.golang.org/p/wfmdobgCUF
答案1
得分: 20
简短回答:
它将切片容量舍入到填充分配的内存块。
长回答:
让我们来看一下Go1.5.1的源代码:https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/cmd/compile/internal/gc/walk.go#L2895 告诉我们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))
我们感兴趣的部分,growslice_n
在这里定义:https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/slice.go#L36
深入一点,我们找到了这个:
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))
roundupsize
在这里定义:https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/msize.go#L178
// 返回mallocgc将分配的内存块的大小。
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)
}
它是在这里引入的:https://groups.google.com/forum/#!topic/golang-codereviews/bFGtI4Cpb_M
在增长切片时,考虑分配的内存块的大小。
英文:
Short answer
It is rounding up the slice capacity to fill the allocated memory blocks.
Long answer
Let's have a look into the Go1.5.1 source code :
https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/cmd/compile/internal/gc/walk.go#L2895 tells us that append(l1, l2...)
is expanded to
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))
The part we are interested in, growslice_n
, is defined there : https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/slice.go#L36
Going a bit deeper, we find this :
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))
roundupsize
is defined there : https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/msize.go#L178
// Returns size of the memory block that mallocgc will allocate if you ask for the size.
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)
}
And it was introduced there : https://groups.google.com/forum/#!topic/golang-codereviews/bFGtI4Cpb_M
> When growing slice take into account size of the allocated memory block.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论