C++では、 memset を使用して、ある値で配列を初期化できます。
_const int MAX = 1000000;
int is_prime[MAX]
memset(is_prime, 1, sizeof(is_prime))
_
Memsetが行うことは、大まかに言って、配列を何らかの値で埋めることとして説明できますが、これを行うのは非常に高速です。
移動中にis_prime := make([]int, 1000000)
を実行できますが、これにより、new([1000000]int)
を使用できるのと同様の方法で、すべて0のスライスが作成されますが、配列/スライスを作成することはできません。すべて1またはその他のゼロ以外の要素。
もちろん、ループを使用して後で値を入力することもできますが、memset
の主な目的は、ループよりもはるかに高速であることです。
では、Goプログラマーはmemset
アナログ(配列をゼロ以外の値に初期化する高速な方法)を持っていますか?
ループを使用した最も単純なソリューションは次のようになります。
_func memsetLoop(a []int, v int) {
for i := range a {
a[i] = v
}
}
_
標準ライブラリにはmemset
のサポートはありませんが、高度に最適化された組み込みの copy()
を利用できます。
copy()
最初の要素を手動で設定し、copy()
を使用してすでに設定されている部分を未設定の部分にコピーし始めることができます。ここで、すでに設定されている部分は毎回大きくなる(2倍になる)ため、反復回数は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倍高速です。
このバグ 「optimizememset idiom」というタイトルの場合、Goではループ以外にこれを行う方法はありません。この投稿により、2013年1月9日に問題は解決されました。
これは修正されたと思います。ゼロ以外のケースを最適化することはあまり面白くありません。
人々がもっとやることを強く望んでいるなら、私たちは別のバグを開くことができます。
したがって、解決策は、すでに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
}