2009-07-04 11 views
41

W kilku miejscach przeczytałem, że maksymalny rozmiar instancji dla struct powinien wynosić 16 bajtów.Dlaczego struktura .NET struct powinna mieć mniej niż 16 bajtów?

Ale nie widzę, skąd pochodzi ta liczba (16).

Przeglądając wokół netto, Znalazłem tacy, którzy sugerują, że jest to liczba przybliżona do dobrych wyników, ale Microsoft mówić jak jest to trudne górna granica. (Np MSDN)

Czy ktoś ma ostateczną odpowiedź, dlaczego to jest 16 bajtów?

Odpowiedz

48

To tylko zasada działania.

Chodzi o to, że ponieważ typy wartości są przekazywane przez wartość, cały rozmiar struktury musi zostać skopiowany, jeśli jest przekazywany do funkcji, podczas gdy dla typu odniesienia, tylko odwołanie (4 bajty) musi być skopiowane. Konstrukcja może jednak zaoszczędzić trochę czasu, ponieważ usuwa się warstwę pośrednią, więc nawet jeśli jest ona większa niż 4 bajty, może być jeszcze wydajniejsza niż przekazywanie odniesienia. Ale w pewnym momencie staje się tak duży, że koszt kopiowania staje się zauważalny. I powszechna zasada jest taka, że ​​zazwyczaj dzieje się to około 16 bajtów. Wybrano 16, ponieważ jest to ładny numer okrągły, potęga dwóch, a alternatywy to albo 8 (które jest zbyt małe i sprawiłoby, że struktury są prawie bezużyteczne), albo 32 (w którym to momencie koszt kopiowania struktury jest już problematyczny jeśli używasz struktur ze względów wydajnościowych)

Ale ostatecznie to jest porady dotyczące wydajności. Odpowiada na pytanie "który byłby najbardziej efektywny w użyciu? Struktura lub klasa?". Ale nie odpowiada na pytanie "co najlepiej odwzorowuje moją domenę problemową".

Struktury i klasy zachowują się inaczej. Jeśli potrzebujesz zachowania struktury, to chciałbym powiedzieć, żeby była to struktura, bez względu na rozmiar. Przynajmniej dopóki nie napotkasz problemów z wydajnością, zrób profil swojego kodu i stwórz problem ze strukturą.

swój link nawet mówi, że to tylko kwestia wykonania:

Jeśli jeden lub więcej z tych warunków nie są spełnione utworzyć rodzaj odniesienia zamiast struktury. Niepowodzenie pod numerem może negatywnie wpłynąć na wydajność.

+1

Tak, link mówi, że jest to kwestia wydajności, ale jest również dość mocny w używanym języku, np. "Nie definiuj struktury ...". Mogli powiedzieć: "To nie jest zalecane ..." – Joe

+0

To prawda, że ​​brzmienie wydaje się nieco mocne. Ale może to być podkreślenie, że klasy przydzielane stertom nie są wolne (jak mogą spodziewać się programiści wywodzący się z języka C/C). – jalf

+4

Jedną z prawdopodobnych odpowiedzi dla dokładnej liczby jest to, że 16-bajtowa struktura jest wciąż wystarczająco mała, aby zmieścić się na procesorze. Magistrala pamięci lub do kopiowania jako część instrukcji SIMD. Większe struktury stają się bardziej skomplikowane do kopiowania lub odczytu/zapisu. – jalf

0

Myślę, że 16 bajtów jest po prostu regułą z punktu widzenia wydajności. Obiekt w .NET korzysta z co najmniej 24 bajtów pamięci (IIRC), więc jeśli twoja struktura jest znacznie większa, preferowany byłby typ odniesienia.

Nie mogę wymyślić żadnego powodu, dla którego wybrały dokładnie 16 bajtów.

+0

* Obiekt w .NET korzysta z co najmniej 24 bajtów pamięci (IIRC) *. Czy masz jakieś odniesienia do tego? – nawfal

21

Wielkość wynika głównie z czasu potrzebnego do skopiowania struktury na stosie, na przykład do przejścia na metodę. Wszystko, co jest o wiele większe i zużywasz dużo miejsca na stos i cykle procesora po prostu kopiując dane - gdy odniesienie do niezmiennej klasy (nawet z dereferencją) może być dużo bardziej wydajne.

22

Jeśli struktura ma nie więcej niż 16 bajtów, można ją skopiować za pomocą kilku prostych instrukcji procesora. Jeśli jest większy, do skopiowania struktury używana jest pętla.

dopóki struktura nie jest większa niż 16 bajty, procesor musi zrobić tej samej pracy, gdy kopiowanie struktury przy kopiowaniu jako odniesienie.Jeśli struktura jest większa, tracisz wydajność wynikającą z posiadania struktury i powinieneś ogólnie uczynić ją klasą.

+1

Nie jestem guru montażu x86, heck Jestem prawdopodobnie kompletnym n00b - ale jestem naprawdę ciekawy, czy mógłbyś zaktualizować swoją odpowiedź za pomocą przykładowego kodu, aby to pokazać? Czy ma znaczenie, czy procesor działa w trybie 32-bitowym w porównaniu z 64-bitowym? – Goyuix

+1

@Goyuix: Oczywiście wystąpią pewne różnice w wydajności między kodem 32-bitowym i 64-bitowym, ale są zgodne z tymi samymi zasadami. Zrobiłem test wydajności jakiś czas temu.Kod jest po prostu dużo struktur i dużo pętli, więc nie jest to tak interesujące, ale możesz zobaczyć rezultat tutaj: http://stackoverflow.com/questions/2437925/why-is-struct-better-with- being-less-than-16-bajtów/2437938 # 2437938 – Guffa

+1

Wydaje mi się, że jest to jedna właściwa odpowiedź i nie jestem pewien, dlaczego została przeoczona. Ta tabela porównująca perfekcję z twoją drugą odpowiedzią jest bardzo wymowna. –

2

Tutaj jest scenariusz, w którym elemencie mogą wykazywać wysoką wydajność:

Kiedy trzeba utworzyć 1000s przypadkach. W tym przypadku, jeśli chcesz użyć klasy, musisz najpierw przydzielić tablicę, aby pomieścić 1000 wystąpień, a następnie w pętli przydzielić każdą instancję. Ale zamiast tego, jeśli użyjesz struktur, to tysiące instancji staną się dostępne natychmiast po przydzieleniu tablicy, która je zatrzyma.

Ponadto, struktury są bardzo przydatne, gdy trzeba wykonać współdziałanie lub chcą zanurzyć się w niebezpieczny kod ze względu na wydajność.

Jak zawsze istnieje kompromis i trzeba przeanalizować, co robią, aby określić najlepszy sposób wdrożenia czegoś.

ps: Ten scenariusz wszedł do gry, gdy pracowałem z danymi LIDAR, gdzie mogły być miliony punktów reprezentujących x, y, z i inne atrybuty dla danych naziemnych. Te dane musiały być załadowane do pamięci w celu przeprowadzenia intensywnych obliczeń, aby uzyskać wydruk wszelkiego rodzaju rzeczy.

7

Jak zauważono w innych odpowiedziach, koszt per-byte kopiowania struktury, która jest większa niż pewien próg (który był 16 bajtów we wcześniejszych wersjach .NET, ale od tego czasu wzrósł do 20-24) jest znacznie większy niż koszt per-byte mniejszej struktury. Należy jednak pamiętać, że skopiowanie struktury o konkretnym rozmiarze będzie ułamkiem kosztu tworzenia nowej instancji obiektu klasy o tym samym rozmiarze. Jeśli struktura byłaby wielokrotnie kopiowana podczas jej trwania, a semantyka typu wartości nie jest szczególnie wymagana, obiekt klasy może być preferowany. Jeśli jednak struktura zostanie skopiowana tylko raz lub dwa razy, takie kopiowanie będzie prawdopodobnie tańsze niż stworzenie nowego obiektu klasy. Liczba kopii liczby kopii, w których obiekt klasy stałby się tańszy, różni się w zależności od rozmiaru danej struktury/obiektu, ale jest znacznie wyższa w przypadku rzeczy, które są poniżej progu "taniego kopiowania", niż w przypadku elementów powyżej.

BTW, innym punktem, o którym warto wspomnieć, jest to, że koszt przekazania struktury jako parametru ref jest niezależny od rozmiaru struktury. W wielu przypadkach można osiągnąć optymalną wydajność, stosując typy wartości i przekazując je przez ref. Należy jednak uważać, aby nie używać właściwości lub pól typów struktury, ponieważ dostęp do jednego z nich spowoduje utworzenie niejawnej tymczasowej kopii danej struktury.

+1

Nie znałem tej części o używaniu właściwości lub pól 'readonly'. Czy możesz wskazać mi link do dalszego czytania? –

+1

@Justin: właściwość getter jest niczym więcej niż metodą, której typem powrotu jest typ właściwości, o której mowa. W związku z tym właściwość musi kopiować informacje do dowolnego rejestru lub lokalizacji pamięci używanych do zwracanej wartości. Pola tylko do odczytu są kopiowane podczas uzyskiwania dostępu do jakichkolwiek właściwości lub metod, ponieważ kompilator nie może wiedzieć, czy wywoływana metoda może próbować zmienić strukturę, w której jest wywoływana. W przeciwieństwie do C#, .net nie ma mechanizmu, który mógłby uniemożliwić metodę struct zmiany struktury, która została do niego przekazana; Microsoft mógł poradzić sobie z tą sytuacją na dwa sposoby: – supercat

+1

(1) Pozwalać na przekazywanie struktur tylko do odczytu bezpośrednio metodom i mieć nadzieję, że nikt nie próbuje przekazać ich metodom mutacji lub (2) zrobić tymczasową kopię tylko do odczytu i przekazuje tę kopię do wywoływanej metody. Zauważ, że (2) będzie wolniejszy niż (1) i generalnie nie da prawidłowego zachowania w przypadkach, w których (1) również nie; co (2) powoduje zmianę natury niewłaściwego zachowania, gdy podejmowana jest próba zmiany metody tylko do odczytu. – supercat

Powiązane problemy