Pertanyaan Kinerja: Irisan struct vs. irisan pointer ke struct


Saya sering bekerja dengan irisan struct. Berikut ini contoh untuk struct seperti itu:

type MyStruct struct {
    val1, val2, val3    int
    text1, text2, text3 string
    list                []SomeType
}

Jadi saya mendefinisikan irisan saya sebagai berikut:

[]MyStruct

Katakanlah saya memiliki sekitar satu juta elemen di sana dan saya bekerja keras dengan potongan:

  • Saya sering menambahkan elemen baru. (Jumlah total elemen tidak diketahui.)
  • Saya menyortirnya setiap sekarang dan kemudian.
  • Saya juga menghapus elemen (meskipun tidak sebanyak menambahkan elemen baru).
  • Saya sering membaca elemen dan menyebarkannya (sebagai argumen fungsi).
  • Isi dari elemen itu sendiri tidak berubah.

Pemahaman saya adalah bahwa ini mengarah ke banyak menyeret sekitar dari struct yang sebenarnya. Alternatifnya adalah membuat potongan pointer ke struct:

[]*MyStruct

Sekarang struct tetap berada di tempat mereka dan kami hanya berurusan dengan pointer yang saya anggap memiliki footprint yang lebih kecil dan karena itu akan membuat operasi saya lebih cepat. Tapi sekarang saya memberi para pengumpul sampah lebih banyak pekerjaan.

  • Dapatkah Anda memberikan pedoman umum kapan harus bekerja dengan struct secara langsung vs. kapan bekerja dengan pointer ke struct?
  • Haruskah saya khawatir tentang berapa banyak pekerjaan yang saya serahkan kepada GC?
  • Apakah overhead kinerja menyalin struct vs menyalin pointer dapat diabaikan?
  • Mungkin sejuta elemen tidak banyak. Bagaimana semua ini berubah ketika irisan menjadi jauh lebih besar (tetapi masih sesuai dengan RAM, tentu saja)?

32
2017-12-23 14:12


asal


Jawaban:


Hanya ingin tahu tentang ini sendiri. Buat beberapa tolok ukur:

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{})
    }
}

Hasil:

BenchmarkAppendingStructs  1000000        3528 ns/op
BenchmarkAppendingPointers 5000000         246 ns/op

Ambil aways: kita berada dalam nanodetik. Mungkin dapat diabaikan untuk irisan kecil. Tapi untuk jutaan ops, itu perbedaan antara milidetik dan mikrodetik.

Btw, saya mencoba menjalankan patokan lagi dengan irisan yang pra-dialokasikan (dengan kapasitas 1000000) untuk menghilangkan overhead dari append () secara berkala menyalin array yang mendasari. Menambahkan structs menurun 1000ns, menambahkan pointer tidak berubah sama sekali.


20
2018-06-03 18:51



Dapatkah Anda memberikan pedoman umum kapan harus bekerja dengan struct secara langsung vs. kapan bekerja dengan pointer ke struct?

Tidak, itu terlalu tergantung pada semua faktor lain yang sudah Anda sebutkan.

Satu-satunya jawaban yang nyata adalah: patokan dan lihat. Setiap kasus berbeda dan semua teori di dunia tidak membuat perbedaan ketika Anda memiliki waktu yang tepat untuk bekerja.

(Yang mengatakan, intuisi saya akan menggunakan pointer, dan mungkin a sync.Pool untuk membantu pengumpul sampah: http://golang.org/pkg/sync/#Pool)


6
2017-12-23 14:20