清除截断指针的更快或更慢的方法?
A faster or slower way to clear truncated pointers?
在我最近读到的truncate
实现中,作者使用以下方式清除截断的项目:
var nilItems = make(items, 16)
func (s *items) truncate(index int) {
var toClear items
*s, toClear = (*s)[:index], (*s)[index:]
for len(toClear) > 0 {
toClear = toClear[copy(toClear, nilItems):]
}
}
当我需要清除不需要的项目时,我会遍历切片并将项目一项一项地设置为 nil
。
我已经设置了一个简单的 benchmark 并且 for
循环方式似乎更快。
我想知道用copy
批量清算有什么好处。
如@MartinGallagher 所述,您的循环已被编译器识别和优化,而 copy()
版本 "too much stuff" 并且未优化。
如果您更改示例以填充非 nil
指针值,您会看到循环版本落后。也不要在基准循环内分配 (make()
),在外部分配,并使用 b.ResetTimer()
排除那个时间。
你也有一个非常小的切片,如果你增加它的大小,差异会更明显:
var x = new(int)
func BenchmarkSetNilOneByOne(b *testing.B) {
nums := make([]*int, 12800)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for i := range nums {
nums[i] = x
}
}
}
func BenchmarkSetNilInBulk(b *testing.B) {
nils := make([]*int, 128)
for i := range nils {
nils[i] = x
}
orig := make([]*int, 12800)
var nums []*int
b.ResetTimer()
for i := 0; i < b.N; i++ {
nums = orig
for len(nums) > 0 {
nums = nums[copy(nums, nils):]
}
}
}
基准测试结果:
BenchmarkSetNilOneByOne-4 96571 10626 ns/op
BenchmarkSetNilInBulk-4 266690 4023 ns/op
另请注意,您的 "bulk" 版本还多次将切片 headers 分配给 nums
。有一种更快的填充切片的方法:您不需要额外的 "nils" 切片,只需开始填充您的切片,您可以将已填充的部分复制到未填充的部分。这也不需要更改/重新分配给 nums
切片 header。参见 Is there analog of memset in go?
在我最近读到的truncate
实现中,作者使用以下方式清除截断的项目:
var nilItems = make(items, 16)
func (s *items) truncate(index int) {
var toClear items
*s, toClear = (*s)[:index], (*s)[index:]
for len(toClear) > 0 {
toClear = toClear[copy(toClear, nilItems):]
}
}
当我需要清除不需要的项目时,我会遍历切片并将项目一项一项地设置为 nil
。
我已经设置了一个简单的 benchmark 并且 for
循环方式似乎更快。
我想知道用copy
批量清算有什么好处。
如@MartinGallagher 所述,您的循环已被编译器识别和优化,而 copy()
版本 "too much stuff" 并且未优化。
如果您更改示例以填充非 nil
指针值,您会看到循环版本落后。也不要在基准循环内分配 (make()
),在外部分配,并使用 b.ResetTimer()
排除那个时间。
你也有一个非常小的切片,如果你增加它的大小,差异会更明显:
var x = new(int)
func BenchmarkSetNilOneByOne(b *testing.B) {
nums := make([]*int, 12800)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for i := range nums {
nums[i] = x
}
}
}
func BenchmarkSetNilInBulk(b *testing.B) {
nils := make([]*int, 128)
for i := range nils {
nils[i] = x
}
orig := make([]*int, 12800)
var nums []*int
b.ResetTimer()
for i := 0; i < b.N; i++ {
nums = orig
for len(nums) > 0 {
nums = nums[copy(nums, nils):]
}
}
}
基准测试结果:
BenchmarkSetNilOneByOne-4 96571 10626 ns/op
BenchmarkSetNilInBulk-4 266690 4023 ns/op
另请注意,您的 "bulk" 版本还多次将切片 headers 分配给 nums
。有一种更快的填充切片的方法:您不需要额外的 "nils" 切片,只需开始填充您的切片,您可以将已填充的部分复制到未填充的部分。这也不需要更改/重新分配给 nums
切片 header。参见 Is there analog of memset in go?