web-dev-qa-db-ja.com

goにmemsetのアナログはありますか?

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アナログ(配列をゼロ以外の値に初期化する高速な方法)を持っていますか?

21
Salvador Dali

ループを使用した最も単純なソリューションは次のようになります。

_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倍高速です。

15
icza

このバグ 「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
}
6
IamNaN