在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
模拟(快速将数组初始化为非零值)?
答案 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)" />