2016-07-29 21 views
1

スライスの追加操作を高速化するには、十分な容量を割り当てる必要があります。スライスを追加するには2つの方法があります、ここではコードです:Golangスライスの追加パフォーマンス

func BenchmarkSliceAppend(b *testing.B) { 
    a := make([]int, 0, b.N) 
    for i := 0; i < b.N; i++ { 
     a = append(a, i) 
    } 
} 

func BenchmarkSliceSet(b *testing.B) { 
    a := make([]int, b.N) 
    for i := 0; i < b.N; i++ { 
     a[i] = i 
    } 
} 

、結果は次のとおりです。

BenchmarkSliceAppend-4 200000000 7.87 NS/OP 8 B/OP 0 allocs/OP

BenchmarkSliceSet-4 300000000 5.76 NS/OP 8 B/OP

a[i] = ia = append(a, i)よりも高速であり、私は理由を知りたいですか?

+3

第がはるか複雑な操作であるスライスに付加されつつ、第1プレーンの割り当てです。それは尋ねるようなものです。「上り坂を歩くことは、椅子に座るよりもはるかに疲れています。なぜですか?" – Volker

+1

あなたは"スライスを追加するには2つの方法があります、ここにコードがあります "と書いています。私の質問は「コードの第2の方法はどこですか?」 – mezoni

+1

https://github.com/EricMountain/golang-benchmark- append –

答えて

6

a[i] = iは、単に値ia[i]に割り当てます。これはではなく、が追加されています。それは単なる簡単なassignmentです。

は今追記:理論的には

a = append(a, i) 

次の処理が行われます。

  1. これは組み込みappend()関数を呼び出します。そのためには、最初にaスライス(スライスヘッダ、バッキングアレイはヘッダの一部ではない)をコピーしなければならず、値iを含むvariadicパラメータの一時スライスを作成する必要があります。 append()aに新しいスライスを割り当てる必要 -

  2. はその後、それがa = a[:len(a)+1]のような十分な容量を(それはあなたのケースで持っている)を持っていればaをresliceする必要があります。
    aに「インプレース」を追加するのに十分な容量がない場合は、新しい配列を割り当て、スライスのコンテンツをコピーしてからassign/appendを実行する必要があります。ここでのケースでは。)

  3. その後a[len(a)-1]からiを割り当てます。

  4. 次に、append()から新しいスライスを返します。この新しいスライスは、ローカル変数aに割り当てられます。

ここでは、単純な割り当てに比べて多くのことが起こります。これらのステップの多くが最適化および/またはインライン化されていても、iをスライスの要素に割り当てるための最小限の追加として、スライスタイプのローカル変数a(スライスヘッダ)をそれぞれ更新する必要がありますループのサイクル。

推奨読書:The Go Blog: Arrays, slices (and strings): The mechanics of 'append'

関連する問題