去掉memset的模拟吗?

时间:2015-06-03 08:00:22

标签: go slice memset

在C ++中,我可以使用memset初始化一个具有某个值的数组:

const int MAX = 1000000;
int is_prime[MAX]

memset(is_prime, 1, sizeof(is_prime))

memset做了什么,粗略地可以被描述为用一些值填充数组,但这样做真的非常快。

在go中,我可以is_prime := make([]int, 1000000),但这将创建一个全0的切片,以类似的方式使用new([1000000]int),但没有什么可以让我创建一个包含所有的数组/切片1或任何其他非零元素。

当然我可以使用循环用稍后的值填充它,但memset的主要目的是它比循环更快。

Go程序员有memset模拟(快速将数组初始化为非零值)?

2 个答案:

答案 0 :(得分:15)

带循环的最简单解决方案如下所示:

func memsetLoop(a []int, v int) {
    for i := range a {
        a[i] = v
    }
}

标准库中没有memset支持,但我们可以使用高度优化的内置copy()

重复copy()

我们可以手动设置第一个元素,并使用copy()开始将已设置的部分复制到未设置的部分;已经设置的部分每次都变得越来越大(双精度),所以迭代次数为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()

如果小切片memsetRepeat()可能比memsetLoop()慢(但在小切片的情况下,它并不重要,它会在瞬间运行)。

由于使用快速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倍

答案 1 :(得分:6)

根据标题为“优化memset惯用法”的this bug,除了循环之外,没有办法在Go中执行此操作。该问题于2013年1月9日以此文章结束

  

我认为这是固定的。优化非零案例并不是很好   有趣。

     

如果人们对做得更多感到强烈,我们可以打开另一个错误。

所以解决方案是使用icza已经涵盖的循环。

bytes.Repeat,但也只是使用循环:

<Import Project ="$(WixTargetsPath)" />