一尘不染

gos中有类似memset吗?

go

在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或任何其他非零元素的数组/切片。

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

那么Go程序员是否有一个memset模拟(将数组初始化为一些非零值的快速方法)?


阅读 279

收藏
2020-07-02

共1个答案

一尘不染

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

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()(但如果是小片,这并不重要,它将立即运行)。

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

2020-07-02