在C ++中,我可以使用memset初始化具有某些值的数组:
const int MAX = 1000000; int is_prime[MAX] memset(is_prime, 1, sizeof(is_prime))
memset所做的工作可以粗略地描述为用一些值填充数组,但是这样做确实非常快。
我可以这样做is_prime := make([]int, 1000000),但这将创建一个全为0的切片,以类似于我可以使用的方式new([1000000]int),但是没有任何东西将允许我创建全为1或任何其他非零元素的数组/切片。
is_prime := make([]int, 1000000)
new([1000000]int)
当然,我可以在以后使用循环用值填充它,但是它的主要目的memset是它比循环快得多。
memset
那么Go程序员是否有一个memset模拟(将数组初始化为一些非零值的快速方法)?
带有循环的最简单的解决方案如下所示:
func memsetLoop(a []int, v int) { for i := range a { a[i] = v } }
memset标准库中没有支持,但是我们可以使用copy()高度优化的内置函数。
copy()
我们可以手动设置第一个元素,然后使用copy();开始将已设置的部分复制到未设置的部分。每次已经设置的部分越来越大(两倍),因此迭代次数为log(n):
log(n)
func memsetRepeat(a []int, v int) { if len(a) == 0 { return } a[0] = v for bp := 1; bp < len(a); bp *= 2 { copy(a[bp:], a[:bp]) } }
此解决方案的灵感来自的实现bytes.Repeat()。如果只想创建一个[]byte填充相同值的新值,则可以使用该bytes.Repeat()函数。您不能将它用于现有的切片或其他切片[]byte,因为您可以使用呈现的切片memsetRepeat()。
bytes.Repeat()
[]byte
memsetRepeat()
如果是小片,memsetRepeat()可能会慢一些memsetLoop()(但如果是小片,这并不重要,它将立即运行)。
memsetLoop()
由于使用fast copy(),memsetRepeat()如果元素数量增加,将会更快。
对这两个解决方案进行基准测试:
var a = make([]int, 1000) // Size will vary func BenchmarkLoop(b *testing.B) { for i := 0; i < b.N; i++ { memsetLoop(a, 10) } } func BenchmarkRepeat(b *testing.B) { for i := 0; i < b.N; i++ { memsetRepeat(a, 11) } }
100个元素: 快约1.15倍
BenchmarkLoop 20000000 81.6 ns/op BenchmarkRepeat 20000000 71.0 ns/op
1,000个元素: 快约2.5倍
BenchmarkLoop 2000000 706 ns/op BenchmarkRepeat 5000000 279 ns/op
10,000个元素: 快2倍
BenchmarkLoop 200000 7029 ns/op BenchmarkRepeat 500000 3544 ns/op
100,000个元素: 快约1.5倍
BenchmarkLoop 20000 70671 ns/op BenchmarkRepeat 30000 45213 ns/op
最高的性能提升约为3800-4000个元素, 速度提高了约3.2倍 。