Adakah yang tahu jawabannya dan / atau punya pendapat tentang ini?
Karena tupel biasanya tidak terlalu besar, saya akan berasumsi akan lebih masuk akal untuk menggunakan struct daripada kelas untuk ini. Apa yang kamu katakan?
Adakah yang tahu jawabannya dan / atau punya pendapat tentang ini?
Karena tupel biasanya tidak terlalu besar, saya akan berasumsi akan lebih masuk akal untuk menggunakan struct daripada kelas untuk ini. Apa yang kamu katakan?
Jawaban:
Microsoft membuat semua jenis referensi jenis tupel untuk kepentingan kesederhanaan.
Saya pribadi berpikir ini adalah kesalahan. Tupel dengan lebih dari 4 bidang sangat tidak biasa dan harus diganti dengan alternatif yang lebih bertipe (seperti jenis rekaman di F #) sehingga hanya tupel kecil yang menarik. Tolok ukur saya sendiri menunjukkan bahwa tupel tanpa kotak hingga 512 byte masih bisa lebih cepat daripada tupel kotak.
Meskipun efisiensi memori adalah salah satu perhatian, saya yakin masalah dominan adalah overhead pengumpul sampah .NET. Alokasi dan pengumpulan sangat mahal di .NET karena pengumpul sampahnya belum dioptimalkan secara maksimal (misalnya dibandingkan dengan JVM). Selain itu, default .NET GC (workstation) belum diparalelkan. Akibatnya, program paralel yang menggunakan tupel menggiling menjadi berhenti karena semua inti bersaing untuk pengumpul sampah bersama, menghancurkan skalabilitas. Ini bukan hanya perhatian yang dominan tetapi, AFAIK, sama sekali diabaikan oleh Microsoft ketika mereka memeriksa masalah ini.
Kekhawatiran lainnya adalah pengiriman virtual. Jenis referensi mendukung subtipe dan, oleh karena itu, anggotanya biasanya dipanggil melalui pengiriman virtual. Sebaliknya, tipe nilai tidak dapat mendukung subtipe sehingga pemanggilan anggota sepenuhnya tidak ambigu dan selalu dapat dilakukan sebagai panggilan fungsi langsung. Pengiriman virtual sangat mahal pada perangkat keras modern karena CPU tidak dapat memprediksi di mana penghitung program akan berakhir. JVM berusaha keras untuk mengoptimalkan pengiriman virtual tetapi .NET tidak. Namun, .NET menyediakan jalan keluar dari pengiriman virtual dalam bentuk tipe nilai. Jadi merepresentasikan tupel sebagai tipe nilai, sekali lagi, dapat meningkatkan kinerja secara dramatis di sini. Misalnya, meneleponGetHashCode
pada 2-tupel satu juta kali membutuhkan 0,17 tetapi memanggilnya pada struct yang setara hanya membutuhkan 0,008s, yaitu tipe nilai 20x lebih cepat dari tipe referensi.
Situasi nyata di mana masalah kinerja dengan tupel biasanya muncul adalah dalam penggunaan tupel sebagai kunci dalam kamus. Saya benar-benar menemukan utas ini dengan mengikuti tautan dari pertanyaan Stack Overflow F # menjalankan algoritme saya lebih lambat dari Python! di mana program F # penulis ternyata lebih lambat dari Python-nya justru karena dia menggunakan tupel kotak. Unboxing secara manual menggunakan tipe tulisan tangan struct
membuat program F #-nya beberapa kali lebih cepat, dan lebih cepat dari Python. Masalah ini tidak akan pernah muncul jika tupel diwakili oleh tipe nilai dan bukan tipe referensi untuk memulai ...
Tuple<_,...,_>
jenisnya bisa saja disegel, dalam hal ini tidak ada pengiriman virtual yang diperlukan meskipun merupakan jenis referensi. Saya lebih penasaran tentang mengapa mereka tidak disegel daripada mengapa mereka adalah tipe referensi.
Alasannya kemungkinan besar karena hanya tupel yang lebih kecil yang masuk akal sebagai tipe nilai karena mereka memiliki jejak memori yang kecil. Tupel yang lebih besar (yaitu yang memiliki lebih banyak properti) sebenarnya akan mengalami penurunan performa karena ukurannya lebih besar dari 16 byte.
Daripada memiliki beberapa tuple menjadi tipe nilai dan yang lain menjadi tipe referensi dan memaksa pengembang untuk mengetahui mana yang menurut saya akan dibayangkan oleh orang-orang di Microsoft bahwa membuat mereka semua tipe referensi lebih sederhana.
Ah, kecurigaan dikonfirmasi! Silakan lihat Building Tuple :
Keputusan besar pertama adalah apakah akan memperlakukan tupel baik sebagai referensi atau tipe nilai. Karena mereka tidak dapat diubah setiap kali Anda ingin mengubah nilai tupel, Anda harus membuat yang baru. Jika mereka adalah tipe referensi, ini berarti ada banyak sampah yang dihasilkan jika Anda mengubah elemen dalam tupel dalam loop yang ketat. F # tupel adalah tipe referensi, tetapi ada perasaan dari tim bahwa mereka dapat mewujudkan peningkatan kinerja jika dua, dan mungkin tiga, elemen tupel adalah tipe nilai. Beberapa tim yang telah membuat tupel internal menggunakan nilai alih-alih tipe referensi, karena skenario mereka sangat sensitif untuk membuat banyak objek terkelola. Mereka menemukan bahwa menggunakan tipe nilai memberi mereka kinerja yang lebih baik. Dalam draf pertama spesifikasi tupel kami, kami mempertahankan tupel dua, tiga, dan empat elemen sebagai tipe nilai, dengan sisanya menjadi tipe referensi. Namun, selama pertemuan desain yang menyertakan perwakilan dari bahasa lain diputuskan bahwa desain "terpisah" ini akan membingungkan, karena semantik yang sedikit berbeda antara kedua jenis tersebut. Konsistensi dalam perilaku dan desain ditentukan untuk menjadi prioritas yang lebih tinggi daripada potensi peningkatan kinerja. Berdasarkan masukan ini, kami mengubah desain sehingga semua tupel adalah tipe referensi, meskipun kami meminta tim F # untuk melakukan investigasi kinerja untuk melihat apakah mengalami percepatan saat menggunakan tipe nilai untuk beberapa ukuran tupel. Itu cara yang baik untuk menguji ini, karena kompilernya, ditulis dalam F #, adalah contoh bagus dari program besar yang menggunakan tupel dalam berbagai skenario. Pada akhirnya, tim F # menemukan bahwa itu tidak mendapatkan peningkatan kinerja ketika beberapa tupel adalah tipe nilai dan bukan tipe referensi. Ini membuat kami merasa lebih baik tentang keputusan kami untuk menggunakan jenis referensi untuk tuple.
Jika tipe .NET System.Tuple <...> didefinisikan sebagai struct, mereka tidak akan dapat diskalakan. Misalnya, tupel terner dari bilangan bulat panjang saat ini diskalakan sebagai berikut:
type Tuple3 = System.Tuple<int64, int64, int64>
type Tuple33 = System.Tuple<Tuple3, Tuple3, Tuple3>
sizeof<Tuple3> // Gets 4
sizeof<Tuple33> // Gets 4
Jika tupel terner didefinisikan sebagai struct, hasilnya adalah sebagai berikut (berdasarkan contoh uji yang saya implementasikan):
sizeof<Tuple3> // Would get 32
sizeof<Tuple33> // Would get 104
Karena tupel memiliki dukungan sintaks built-in dalam F #, dan mereka sangat sering digunakan dalam bahasa ini, tupel "struct" akan membuat programmer F # berisiko menulis program yang tidak efisien bahkan tanpa menyadarinya. Itu akan terjadi dengan sangat mudah:
let t3 = 1L, 2L, 3L
let t33 = t3, t3, t3
Menurut pendapat saya, tupel "struct" akan menyebabkan probabilitas yang tinggi untuk menciptakan inefisiensi yang signifikan dalam pemrograman sehari-hari. Di sisi lain, tuple "class" yang ada saat ini juga menyebabkan inefisiensi tertentu, seperti yang disebutkan oleh @Jon. Namun, saya berpikir bahwa produk dari "probabilitas kejadian" kali "potensi kerusakan" akan jauh lebih tinggi dengan struct daripada saat ini dengan kelas. Oleh karena itu, implementasi saat ini adalah kejahatan yang lebih rendah.
Idealnya, akan ada tupel "class" dan "struct", keduanya dengan dukungan sintaksis di F #!
Sunting (2017-10-07)
Struct tuple sekarang didukung penuh sebagai berikut:
ref
, atau mungkin tidak menyukai fakta bahwa apa yang disebut "struct yang tidak dapat diubah" tidak, terutama ketika dimasukkan kotak. Sayang sekali .net tidak pernah mengimplementasikan konsep melewatkan parameter oleh sebuah penegakan const ref
, karena dalam banyak kasus semantik seperti itu yang benar-benar dibutuhkan.
Dictionary
, misalnya di sini: stackoverflow.com/questions/5850243 /…
Untuk 2-tupel, Anda masih dapat selalu menggunakan KeyValuePair <TKey, TValue> dari versi Common Type System sebelumnya. Ini adalah tipe nilai.
Klarifikasi kecil untuk artikel Matt Ellis adalah bahwa perbedaan penggunaan semantik antara referensi dan tipe nilai hanya "sedikit" ketika keabadian berlaku (yang, tentu saja, akan menjadi kasus di sini). Namun demikian, saya pikir akan lebih baik dalam desain BCL untuk tidak menimbulkan kebingungan karena Tuple menyeberang ke jenis referensi di ambang tertentu.
Saya tidak tahu tetapi jika Anda pernah menggunakan F # Tuple adalah bagian dari bahasa. Jika saya membuat .dll dan mengembalikan jenis Tuple, alangkah baiknya memiliki jenis untuk dimasukkan. Saya menduga sekarang F # adalah bagian dari bahasa (.Net 4) beberapa modifikasi CLR dibuat untuk mengakomodasi beberapa struktur umum di F #
Dari http://en.wikibooks.org/wiki/F_Sharp_Programming/Tuples_and_Records
let scalarMultiply (s : float) (a, b, c) = (a * s, b * s, c * s);;
val scalarMultiply : float -> float * float * float -> float * float * float
scalarMultiply 5.0 (6.0, 10.0, 20.0);;
val it : float * float * float = (30.0, 50.0, 100.0)
ValueTuple<...>
. Lihat referensi di C # jenis tupel