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?
performance
class
struct
.net-4.0
Bent Rasmussen
sumber
sumber
ValueTuple<...>
. Lihat referensi di C # jenis tupelJawaban:
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, menelepon
GetHashCode
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 ...sumber
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 :
sumber
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:
sumber
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 penegakanconst 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.
sumber
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)
sumber