構造体のスライスを頻繁に使用します。このような構造体の例を次に示します。
type MyStruct struct {
val1, val2, val3 int
text1, text2, text3 string
list []SomeType
}
そこで、スライスを次のように定義します。
[]MyStruct
私はそこに約100万個の要素があり、スライスを大いに使っているとしましょう:
私の理解では、これは実際の構造体の多くのシャッフルにつながります。別の方法は、構造体へのポインタのスライスを作成することです:
[]*MyStruct
構造体は現在の場所に残り、フットプリントが小さく、操作が高速になると思われるポインターのみを処理します。しかし、今はガベージコレクターにもっと多くの仕事をさせています。
自分でこれに興味を持ちました。いくつかのベンチマークを実行しました:
type MyStruct struct {
F1, F2, F3, F4, F5, F6, F7 string
I1, I2, I3, I4, I5, I6, I7 int64
}
func BenchmarkAppendingStructs(b *testing.B) {
var s []MyStruct
for i := 0; i < b.N; i++ {
s = append(s, MyStruct{})
}
}
func BenchmarkAppendingPointers(b *testing.B) {
var s []*MyStruct
for i := 0; i < b.N; i++ {
s = append(s, &MyStruct{})
}
}
結果:
BenchmarkAppendingStructs 1000000 3528 ns/op
BenchmarkAppendingPointers 5000000 246 ns/op
テイクアウェイ:ナノ秒単位です。小さいスライスの場合はおそらく無視できます。しかし、何百万もの操作では、ミリ秒とマイクロ秒の差です。
ところで、私は、基礎となる配列を定期的にコピーするappend()からオーバーヘッドを排除するために、事前に割り当てられたスライス(1000000の容量)でベンチマークを再度実行しようとしました。構造体の追加は1000nsを落としましたが、ポインターの追加はまったく変わりませんでした。
構造体を直接操作する場合と構造体へのポインターを操作する場合の一般的なガイドラインを提供できますか?
いいえ、それはあなたがすでに言及した他のすべての要因に依存しすぎています。
唯一の本当の答えは、ベンチマークと参照です。すべてのケースは異なり、実際のタイミングを調整しても、世界のすべての理論に違いはありません。
(そうは言っても、私の直感ではポインターを使用し、場合によってはsync.Pool
ガベージコレクターを支援するために: http://golang.org/pkg/sync/#Pool )