go 中有 memset 的模拟吗?
Is there analog of memset in go?
在 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)
的类似方式,但我无法创建一个 [=26] =] 与所有 1 或任何其他非零元素。
当然我可以使用循环来稍后用值填充它,但是 memset
的主要目的是它比循环快得多。
Go 程序员也有 memset
模拟(将数组初始化为某个非零值的快速方法)吗?
最简单的循环解决方案如下所示:
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()
会更快。
对这 2 个解决方案进行基准测试:
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 倍。
根据标题为 "optimize memset idiom" 的 this bug,除了使用循环外,在 Go 中没有其他方法可以做到这一点。该问题已于 2013 年 1 月 9 日关闭 post
I consider this fixed. Optimizing non-zero cases isn't very
interesting.
We can open another bug if people feel strongly about doing more.
所以解决方案是使用 icza 已经涵盖的循环。
有 bytes.Repeat 但它也只是使用了一个循环:
func Repeat(b []byte, count int) []byte {
nb := make([]byte, len(b)*count)
bp := copy(nb, b)
for bp < len(nb) {
copy(nb[bp:], nb[:bp])
bp *= 2
}
return nb
}
在 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)
的类似方式,但我无法创建一个 [=26] =] 与所有 1 或任何其他非零元素。
当然我可以使用循环来稍后用值填充它,但是 memset
的主要目的是它比循环快得多。
Go 程序员也有 memset
模拟(将数组初始化为某个非零值的快速方法)吗?
最简单的循环解决方案如下所示:
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()
会更快。
对这 2 个解决方案进行基准测试:
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 倍。
根据标题为 "optimize memset idiom" 的 this bug,除了使用循环外,在 Go 中没有其他方法可以做到这一点。该问题已于 2013 年 1 月 9 日关闭 post
I consider this fixed. Optimizing non-zero cases isn't very interesting.
We can open another bug if people feel strongly about doing more.
所以解决方案是使用 icza 已经涵盖的循环。
有 bytes.Repeat 但它也只是使用了一个循环:
func Repeat(b []byte, count int) []byte {
nb := make([]byte, len(b)*count)
bp := copy(nb, b)
for bp < len(nb) {
copy(nb[bp:], nb[:bp])
bp *= 2
}
return nb
}