Saya akan membuat 100.000 objek dalam kode. Mereka kecil, hanya dengan 2 atau 3 properti. Saya akan menempatkannya dalam daftar umum dan jika sudah, saya akan mengulanginya dan memeriksa nilai a
dan mungkin memperbarui nilai b
.
Apakah lebih cepat / lebih baik untuk membuat objek ini sebagai kelas atau sebagai struct?
EDIT
Sebuah. Properti adalah tipe nilai (kecuali string yang menurut saya?)
b. Mereka mungkin (kami belum yakin) memiliki metode validasi
EDIT 2
Saya bertanya-tanya: apakah objek di heap dan tumpukan diproses secara sama oleh pengumpul sampah, atau apakah cara kerjanya berbeda?
Jawaban:
Anda adalah satu-satunya orang yang dapat menentukan jawaban atas pertanyaan itu. Cobalah kedua cara tersebut, ukur metrik kinerja yang bermakna, berfokus pada pengguna, dan relevan, lalu Anda akan mengetahui apakah perubahan tersebut memiliki efek yang berarti pada pengguna nyata dalam skenario yang relevan.
Struktur mengonsumsi lebih sedikit memori heap (karena lebih kecil dan lebih mudah dipadatkan, bukan karena "ada di tumpukan"). Tetapi mereka membutuhkan waktu lebih lama untuk menyalin daripada salinan referensi. Saya tidak tahu apa metrik kinerja Anda untuk penggunaan atau kecepatan memori; ada pengorbanan di sini dan Anda adalah orang yang tahu apa itu.
Mungkin kelas, mungkin struct. Sebagai aturan praktis: Jika objeknya adalah:
1. Kecil
2. Secara logis, nilai yang tidak dapat diubah
3. Ada banyak nilai .
Maka saya akan mempertimbangkan untuk menjadikannya sebuah struct. Jika tidak, saya akan tetap menggunakan tipe referensi.
Jika Anda perlu mengubah beberapa bidang dari sebuah struct, biasanya lebih baik membuat konstruktor yang mengembalikan struct baru dengan bidang yang disetel dengan benar. Itu mungkin sedikit lebih lambat (ukurlah!) Tetapi secara logis lebih mudah untuk bernalar.
Tidak , keduanya tidak sama karena objek di tumpukan adalah akar dari koleksi . Pengumpul sampah tidak perlu bertanya "apakah benda yang ada di tumpukan ini hidup?" karena jawaban atas pertanyaan itu selalu "Ya, ada di tumpukan". (Sekarang, Anda tidak dapat mengandalkan itu untuk menjaga objek tetap hidup karena tumpukan adalah detail implementasi. Jitter diizinkan untuk memperkenalkan pengoptimalan yang, katakanlah, mendaftarkan apa yang biasanya menjadi nilai tumpukan, dan kemudian tidak pernah ada di tumpukan jadi GC tidak tahu bahwa ia masih hidup. Objek yang didaftarkan dapat dikumpulkan turunannya secara agresif, segera setelah register yang memegangnya tidak akan dibaca lagi.)
Tapi pengumpul sampah tidak harus memperlakukan benda-benda di stack sebagai hidup, dengan cara yang sama bahwa memperlakukan setiap objek diketahui hidup sebagai hidup. Objek di tumpukan bisa merujuk ke objek dengan alokasi heap yang perlu dipertahankan, jadi GC harus memperlakukan objek tumpukan seperti objek hidup dengan alokasi heap untuk tujuan menentukan set langsung. Tapi jelas mereka tidak diperlakukan sebagai "benda hidup" untuk tujuan memadatkan heap, karena mereka tidak berada di heap sejak awal.
Apakah itu jelas?
sumber
readonly
) untuk memungkinkan pengoptimalan. Saya tidak akan membiarkan hal itu memengaruhi pilihan pada mutabilitas (saya gila untuk detail efisiensi dalam teori, tetapi dalam praktiknya langkah pertama saya menuju efisiensi selalu berusaha untuk memiliki jaminan kebenaran sesederhana mungkin dan karenanya tidak perlu buang siklus CPU dan siklus otak pada pemeriksaan dan kasus tepi, dan menjadi yang dapat diubah atau tidak berubah dengan tepat membantu di sana), tetapi itu akan melawan reaksi spontan apa pun terhadap pernyataan Anda yang tidak dapat diubah bisa lebih lambat.Terkadang dengan
struct
Anda tidak perlu memanggil konstruktor new (), dan langsung menetapkan bidang sehingga lebih cepat dari biasanya.Contoh:
sekitar 2 hingga 3 kali lebih cepat dari
dimana
Value
astruct
dengan dua bidang (id
danisValid
).Di sisi lain adalah item perlu dipindahkan atau jenis nilai yang dipilih semua yang menyalin akan memperlambat Anda. Untuk mendapatkan jawaban yang tepat, saya curiga Anda harus membuat profil kode Anda dan mengujinya.
sumber
list
, mengingat kode yang ditunjukkan tidak akan berfungsi dengan fileList<Value>
.Struktur mungkin tampak mirip dengan kelas, tetapi ada perbedaan penting yang harus Anda perhatikan. Pertama-tama, kelas adalah tipe referensi dan struct adalah tipe nilai. Dengan menggunakan struct, Anda dapat membuat objek yang berperilaku seperti tipe bawaan dan menikmati manfaatnya juga.
Saat Anda memanggil operator baru di kelas, itu akan dialokasikan di heap. Namun, saat Anda membuat instance struct, itu dibuat di stack. Ini akan menghasilkan peningkatan kinerja. Juga, Anda tidak akan berurusan dengan referensi ke sebuah instance dari sebuah struct seperti yang Anda lakukan dengan kelas. Anda akan bekerja secara langsung dengan instance struct. Karenanya, saat meneruskan struct ke suatu metode, ia diteruskan oleh nilai, bukan sebagai referensi.
Selengkapnya di sini:
http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx
sumber
Array struct direpresentasikan di heap dalam blok memori yang berdekatan, sedangkan array objek direpresentasikan sebagai blok referensi yang berdekatan dengan objek aktual itu sendiri di tempat lain di heap, sehingga membutuhkan memori untuk objek dan referensi arraynya. .
Dalam hal ini, saat Anda menempatkannya dalam a
List<>
(dan aList<>
didukung ke array), akan lebih efisien, menggunakan memori untuk menggunakan struct.(Berhati-hatilah, bahwa array besar akan menemukan jalannya di Large Object Heap di mana, jika masa pakainya lama, dapat berdampak buruk pada manajemen memori proses Anda. Ingat, juga, bahwa memori bukan satu-satunya pertimbangan.)
sumber
ref
kata kunci untuk menangani ini.Jika mereka memiliki semantik nilai, Anda mungkin harus menggunakan struct. Jika mereka memiliki semantik referensi, Anda mungkin harus menggunakan kelas. Ada pengecualian, yang kebanyakan condong ke arah pembuatan kelas bahkan ketika ada semantik nilai, tetapi mulai dari sana.
Sedangkan untuk pengeditan kedua Anda, GC hanya menangani heap, tetapi ada lebih banyak ruang heap daripada ruang tumpukan, jadi meletakkan sesuatu di tumpukan tidak selalu menguntungkan. Selain itu, daftar tipe-struct dan daftar tipe-kelas akan ada di heap, jadi ini tidak relevan dalam kasus ini.
Edit:
Saya mulai menganggap istilah kejahatan itu berbahaya. Lagi pula, membuat kelas bisa berubah adalah ide yang buruk jika tidak diperlukan secara aktif, dan saya tidak akan mengesampingkan pernah menggunakan struct yang bisa berubah. Ini adalah ide yang buruk sehingga sering kali hampir selalu menjadi ide yang buruk, tetapi sebagian besar itu tidak sesuai dengan nilai semantik sehingga tidak masuk akal untuk menggunakan struct dalam kasus tertentu.
Mungkin ada pengecualian yang wajar dengan struct bertingkat pribadi, di mana semua penggunaan struct itu dibatasi pada cakupan yang sangat terbatas. Ini tidak berlaku di sini.
Sungguh, saya pikir "itu bermutasi jadi ini adalah tindakan yang buruk" tidak jauh lebih baik daripada membahas tentang heap dan tumpukan (yang setidaknya memiliki beberapa dampak kinerja, bahkan jika sering disalahartikan). "Ini bermutasi, jadi sepertinya tidak masuk akal untuk menganggapnya memiliki nilai semantik, jadi ini adalah struct yang buruk" hanya sedikit berbeda, tapi yang terpenting jadi saya pikir.
sumber
Solusi terbaik adalah mengukur, mengukur lagi, lalu mengukur lagi. Mungkin ada detail dari apa yang Anda lakukan yang mungkin membuat jawaban yang sederhana dan mudah seperti "gunakan struktur" atau "kelas penggunaan" menjadi sulit.
sumber
Sebuah struct, pada intinya, tidak lebih dan tidak kurang dari kumpulan bidang. Dalam .NET, mungkin saja struktur "berpura-pura" menjadi objek, dan untuk setiap jenis struktur .NET secara implisit mendefinisikan tipe objek heap dengan bidang dan metode yang sama yang - menjadi objek heap - akan berperilaku seperti objek . Variabel yang menyimpan referensi ke objek heap semacam itu (struktur "kotak") akan menunjukkan semantik referensi, tetapi variabel yang memegang struct secara langsung hanyalah kumpulan variabel.
Saya pikir banyak kebingungan struct-versus-class berasal dari fakta bahwa struktur memiliki dua kasus penggunaan yang sangat berbeda, yang seharusnya memiliki pedoman desain yang sangat berbeda, tetapi pedoman MS tidak membedakannya. Terkadang ada kebutuhan akan sesuatu yang berperilaku seperti objek; dalam hal ini, pedoman MS cukup masuk akal, meskipun "batas 16 byte" mungkin lebih seperti 24-32. Namun terkadang, yang dibutuhkan adalah agregasi variabel. Sebuah struct yang digunakan untuk tujuan itu seharusnya hanya terdiri dari sekumpulan bidang publik, dan mungkin sebuah
Equals
override,ToString
override, danIEquatable(itsType).Equals
penerapan. Struktur yang digunakan sebagai agregasi bidang bukanlah objek, dan tidak boleh berpura-pura menjadi. Dari sudut pandang struktur, arti dari field seharusnya tidak lebih atau kurang dari "hal terakhir yang dituliskan pada field ini". Arti tambahan harus ditentukan oleh kode klien.Misalnya, jika struct pengumpul variabel memiliki anggota
Minimum
danMaximum
, struct itu sendiri seharusnya tidak menjanjikan hal ituMinimum <= Maximum
. Kode yang menerima struktur seperti parameter harus berperilaku seolah-olah diteruskan secara terpisahMinimum
danMaximum
nilai. Persyaratan yangMinimum
tidak lebih besar dariMaximum
harus dianggap seperti persyaratan bahwaMinimum
parameter tidak boleh lebih besar dari yang diteruskan secara terpisahMaximum
.Pola yang berguna untuk dipertimbangkan kadang-kadang adalah membuat
ExposedHolder<T>
kelas mendefinisikan sesuatu seperti:Jika seseorang memiliki
List<ExposedHolder<someStruct>>
, di manasomeStruct
struct pengumpul-variabel, ia dapat melakukan hal-hal seperti itumyList[3].Value.someField += 7;
, tetapi memberikanmyList[3].Value
ke kode lain akan memberikan isinyaValue
daripada memberinya sarana untuk mengubahnya. Sebaliknya, jika seseorang menggunakan aList<someStruct>
, itu akan perlu digunakanvar temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Jika seseorang menggunakan tipe kelas yang bisa berubah, mengekspos kontenmyList[3]
ke kode luar akan memerlukan penyalinan semua bidang ke objek lain. Jika seseorang menggunakan tipe kelas yang tidak bisa diubah, atau struct "object-style", maka perlu untuk membuat sebuah instance baru yang sepertimyList[3]
kecualisomeField
yang berbeda, dan kemudian menyimpan instance baru itu ke dalam daftar.Satu catatan tambahan: Jika Anda menyimpan banyak hal yang serupa, mungkin baik untuk menyimpannya dalam susunan struktur yang mungkin bersarang, lebih disukai mencoba untuk menjaga ukuran setiap larik antara 1K dan 64K atau lebih. Array struktur adalah khusus, dalam pengindeksan itu akan menghasilkan referensi langsung ke struktur di dalamnya, sehingga seseorang dapat mengatakan "a [12] .x = 5;". Meskipun seseorang dapat mendefinisikan objek mirip array, C # tidak mengizinkan mereka untuk berbagi sintaks seperti itu dengan array.
sumber
Gunakan kelas.
Pada catatan umum. Mengapa tidak memperbarui nilai b saat Anda membuatnya?
sumber
Dari perspektif c ++ saya setuju bahwa memodifikasi properti struct lebih lambat dibandingkan dengan kelas. Tapi saya pikir mereka akan lebih cepat dibaca karena struct dialokasikan pada tumpukan daripada heap. Membaca data dari heap membutuhkan lebih banyak pemeriksaan daripada dari tumpukan.
sumber
Nah, jika Anda menggunakan struct afterall, maka singkirkan string dan gunakan char ukuran tetap atau buffer byte.
Itu re: kinerja.
sumber