Czy ktoś zna odpowiedź i / lub ma na ten temat opinię?
Ponieważ krotki normalnie nie byłyby bardzo duże, przypuszczam, że bardziej sensowne byłoby użycie do nich struktur niż klas. Co powiesz?
performance
class
struct
.net-4.0
Bent Rasmussen
źródło
źródło
ValueTuple<...>
. Zobacz odniesienia w C # typy krotekOdpowiedzi:
Firma Microsoft stworzyła wszystkie typy odwołań do typów krotek w celu uproszczenia.
Osobiście uważam, że to był błąd. Krotki z więcej niż 4 polami są bardzo nietypowe i i tak powinny zostać zastąpione bardziej typową alternatywą (taką jak typ rekordu w F #), więc tylko małe krotki są praktyczne. Moje własne testy porównawcze pokazały, że rozpakowane krotki do 512 bajtów mogą być nadal szybsze niż krotki w pudełkach.
Chociaż wydajność pamięci jest jednym z problemów, uważam, że dominującą kwestią jest obciążenie modułu odśmiecania pamięci .NET. Alokacja i zbieranie są bardzo kosztowne w .NET, ponieważ jego moduł odśmiecania pamięci nie został zbyt mocno zoptymalizowany (np. W porównaniu z maszyną JVM). Ponadto domyślny .NET GC (stacja robocza) nie został jeszcze zrównoleglony. W rezultacie równoległe programy, które używają krotek, zatrzymują się, ponieważ wszystkie rdzenie walczą o współdzielony moduł odśmiecania pamięci, niszcząc skalowalność. To nie tylko dominujący problem, ale, AFAIK, został całkowicie zaniedbany przez Microsoft, gdy badał ten problem.
Kolejnym problemem jest wirtualna wysyłka. Typy referencyjne obsługują podtypy, dlatego ich elementy członkowskie są zwykle wywoływane za pośrednictwem wirtualnej wysyłki. W przeciwieństwie do tego typy wartości nie mogą obsługiwać podtypów, więc wywołanie elementu członkowskiego jest całkowicie jednoznaczne i zawsze można je wykonać jako bezpośrednie wywołanie funkcji. Wirtualna wysyłka jest niezwykle kosztowna w przypadku nowoczesnego sprzętu, ponieważ procesor nie może przewidzieć, gdzie skończy się licznik programu. JVM dokłada wszelkich starań, aby zoptymalizować wysyłanie wirtualne, ale .NET tego nie robi. Jednak .NET zapewnia ucieczkę przed wirtualną wysyłką w postaci typów wartości. Zatem reprezentowanie krotek jako typów wartości mogłoby tutaj ponownie znacznie poprawić wydajność. Na przykład dzwonienie
GetHashCode
na dwukrotnej krotce milion razy zajmuje 0,17 s, ale wywołanie jej na równoważnej strukturze zajmuje tylko 0,008 s, tj. typ wartości jest 20 razy szybszy niż typ referencyjny.Prawdziwą sytuacją, w której często występują problemy z wydajnością krotek, jest użycie krotek jako kluczy w słownikach. Właściwie natknąłem się na ten wątek, podążając za linkiem z pytania przepełnienia stosu. F # uruchamia mój algorytm wolniej niż Python! gdzie autorski program F # okazał się wolniejszy niż jego Python właśnie dlatego, że używał krotek pudełkowych. Ręczne rozpakowywanie przy użyciu odręcznego
struct
typu sprawia, że jego program F # jest kilka razy szybszy i szybszy niż Python. Te problemy nigdy by się nie pojawiły, gdyby krotki były reprezentowane przez typy wartości, a nie typy referencyjne na początku ...źródło
Tuple<_,...,_>
typy mogły zostać przypieczętowane, w takim przypadku żadna wirtualna wysyłka nie byłaby potrzebna, mimo że są typami referencyjnymi. Bardziej ciekawi mnie, dlaczego nie są zapieczętowane, niż dlaczego są typami referencyjnymi.Powodem jest najprawdopodobniej to, że tylko mniejsze krotki miałyby sens jako typy wartości, ponieważ miałyby niewielki ślad pamięci. Większe krotki (tj. Te z większą liczbą właściwości) faktycznie ucierpiałyby na wydajności, ponieważ byłyby większe niż 16 bajtów.
Zamiast tego, aby niektóre krotki były typami wartości, a inne były typami referencyjnymi, i zmuszają programistów, aby wiedzieli, które z nich są, jak sobie wyobrażam, ludzie w firmie Microsoft myśleli, że uczynienie ich wszystkich typów referencyjnych było prostsze.
Ach, podejrzenia się potwierdziły! Zobacz sekcję Budowanie :
źródło
Gdyby typy .NET System.Tuple <...> zostały zdefiniowane jako struktury, nie byłyby skalowalne. Na przykład trójskładnikowa krotka długich liczb całkowitych jest obecnie skalowana w następujący sposób:
type Tuple3 = System.Tuple<int64, int64, int64> type Tuple33 = System.Tuple<Tuple3, Tuple3, Tuple3> sizeof<Tuple3> // Gets 4 sizeof<Tuple33> // Gets 4
Gdyby trójskładnikowa krotka została zdefiniowana jako struktura, wynik byłby następujący (na podstawie przykładu testowego, który zaimplementowałem):
sizeof<Tuple3> // Would get 32 sizeof<Tuple33> // Would get 104
Ponieważ krotki mają wbudowaną obsługę składni w języku F # i są niezwykle często używane w tym języku, krotki „struct” narażałyby programistów F # na ryzyko pisania nieefektywnych programów, nawet o tym nie wiedząc. Stałoby się to tak łatwo:
let t3 = 1L, 2L, 3L let t33 = t3, t3, t3
Moim zdaniem krotki „strukturalne” spowodowałyby duże prawdopodobieństwo powstania znacznych nieefektywności w codziennym programowaniu. Z drugiej strony, istniejące obecnie krotki „klasowe” również powodują pewne nieefektywności, o czym wspomniał @Jon. Myślę jednak, że iloczyn „prawdopodobieństwa wystąpienia” razy „potencjalne uszkodzenie” byłby znacznie wyższy w przypadku struktur niż obecnie w przypadku klas. Dlatego obecne wdrożenie jest mniejszym złem.
Idealnie byłoby, gdyby istniały zarówno krotki „class”, jak i „struct”, obie z obsługą składni w języku F #!
Edycja (2017-10-07)
Krotki struktury są teraz w pełni obsługiwane w następujący sposób:
źródło
ref
, lub może nie lubić faktu, że tak zwane „niezmienne struktury” nie są, zwłaszcza gdy są opakowane. Szkoda, że .net nigdy nie zaimplementował koncepcji przekazywania parametrów przez element egzekwowalnyconst ref
, ponieważ w wielu przypadkach taka semantyka jest naprawdę wymagana.Dictionary
, np. Tutaj: stackoverflow.com/questions/5850243 /…W przypadku krotek 2 nadal można zawsze używać KeyValuePair <TKey, TValue> z wcześniejszych wersji systemu Common Type. To typ wartości.
Drobnym wyjaśnieniem artykułu Matta Ellisa byłoby to, że różnica w semantyce użycia między typami referencyjnymi i wartościowymi jest tylko „niewielka”, gdy obowiązuje niezmienność (co oczywiście miałoby miejsce w tym przypadku). Niemniej jednak myślę, że byłoby najlepiej w projekcie BCL nie wprowadzać zamieszania związanego z przejściem Tuple do typu odniesienia na pewnym progu.
źródło
Nie wiem, ale jeśli kiedykolwiek używałeś F # krotki są częścią języka. Jeśli utworzyłem plik .dll i zwróciłem typ krotek, byłoby miło mieć typ do umieszczenia go. Podejrzewam teraz, że F # jest częścią języka (.Net 4) wprowadzono pewne modyfikacje w CLR w celu dostosowania niektórych wspólnych struktur w F #
Z 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)
źródło