Mam zamiar stworzyć 100 000 obiektów w kodzie. Są małe, tylko z 2 lub 3 właściwościami. Umieszczę je na ogólnej liście, a kiedy już są, zapętlę je i sprawdzę wartość, a
a może zaktualizuję wartość b
.
Czy szybciej / lepiej jest tworzyć te obiekty jako klasę czy strukturę?
EDYTOWAĆ
za. Właściwości są typami wartości (poza ciągiem, jak sądzę?)
b. Mogą (nie jesteśmy jeszcze pewni) mieć metodę walidacji
EDYCJA 2
Zastanawiałem się: czy obiekty na stercie i stosie są przetwarzane równo przez garbage collectora, czy to działa inaczej?
Odpowiedzi:
Jesteś jedyną osobą, która może określić odpowiedź na to pytanie. Wypróbuj oba sposoby, zmierz znaczący, zorientowany na użytkownika i odpowiedni miernik wydajności, a dowiesz się, czy zmiana ma znaczący wpływ na prawdziwych użytkowników w odpowiednich scenariuszach.
Struktury zużywają mniej pamięci sterty (ponieważ są mniejsze i łatwiejsze do zagęszczenia, a nie dlatego, że znajdują się „na stosie”). Ale kopiowanie trwa dłużej niż kopia referencyjna. Nie wiem, jakie są wskaźniki wydajności dotyczące wykorzystania pamięci lub szybkości; jest tu kompromis i jesteś osobą, która wie, co to jest.
Może klasa, może struktura. Ogólna reguła: Jeśli obiekt jest:
1. Mały
2. Logicznie niezmienna wartość
3. Jest ich dużo.
Wtedy rozważę zrobienie z niego struktury. W przeciwnym razie trzymałbym się typu referencyjnego.
Jeśli chcesz zmutować jakieś pole struktury, zwykle lepiej jest zbudować konstruktora, który zwraca całą nową strukturę z prawidłowo ustawionym polem. To może być trochę wolniejsze (zmierz to!), Ale logicznie dużo łatwiejsze do rozważenia.
Nie , nie są takie same, ponieważ obiekty na stosie są podstawowymi elementami kolekcji . Odśmiecacz nie musi nigdy pytać „czy ta rzecz na stosie żyje?” ponieważ odpowiedź na to pytanie zawsze brzmi „Tak, jest na stosie”. (Teraz nie możesz na tym polegać, aby utrzymać obiekt przy życiu, ponieważ stos jest szczegółem implementacji. Jitter może wprowadzać optymalizacje, które, powiedzmy, rejestrują to, co normalnie byłoby wartością stosu, a następnie nigdy nie jest na stosie więc GC nie wie, że wciąż żyje. Zarejestrowany obiekt może agresywnie zbierać swoich potomków, gdy tylko rejestr, który go posiada, nie będzie ponownie odczytywany.)
Ale garbage collector nie trzeba traktować obiektów na stosie jako żywa, w taki sam sposób, że traktuje dowolny obiekt znany, że żyje jak żyje. Obiekt na stosie może odnosić się do obiektów przydzielonych na stosie, które muszą być utrzymywane przy życiu, więc GC musi traktować obiekty stosu jak żywe obiekty przydzielone na stosie w celu określenia aktywnego zestawu. Ale oczywiście nie są one traktowane jako „żywe obiekty” na potrzeby kompaktowania pryzmy, ponieważ w ogóle nie znajdują się na stercie.
Czy to jasne?
źródło
readonly
), aby umożliwić optymalizacje. Nie pozwoliłbym, żeby to wpłynęło na wybór zmienności (teoretycznie jestem wariatem na temat szczegółów wydajności, ale w praktyce moim pierwszym krokiem w kierunku wydajności jest zawsze próba uzyskania tak prostej gwarancji poprawności, jak tylko mogę i dlatego marnować cykle procesora i cykle mózgowe na kontrolach i przypadkach skrajnych, a bycie odpowiednio zmiennym lub niezmiennym pomaga tam), ale przeciwdziałałoby to jakiejkolwiek odruchowej reakcji na twoje twierdzenie, że niezmienność może być wolniejsza.Czasami
struct
nie musisz wywoływać konstruktora new () i bezpośrednio przypisywać pola, dzięki czemu jest znacznie szybszy niż zwykle.Przykład:
Value[] list = new Value[N]; for (int i = 0; i < N; i++) { list[i].id = i; list[i].isValid = true; }
jest około 2 do 3 razy szybszy niż
Value[] list = new Value[N]; for (int i = 0; i < N; i++) { list[i] = new Value(i, true); }
gdzie
Value
jeststruct
z dwoma polami (id
iisValid
).struct Value { int id; bool isValid; public Value(int i, bool isValid) { this.i = i; this.isValid = isValid; } }
Z drugiej strony, elementy muszą zostać przeniesione lub wybrane typy wartości, wszystko to kopiowanie spowolni. Podejrzewam, że aby uzyskać dokładną odpowiedź, musisz sprofilować swój kod i przetestować go.
źródło
list
, biorąc pod uwagę, że wskazany kod nie będzie działał zList<Value>
.Struktury mogą wydawać się podobne do klas, ale istnieją istotne różnice, o których powinieneś wiedzieć. Przede wszystkim klasy są typami referencyjnymi, a struktury są typami wartości. Używając struktur, możesz tworzyć obiekty, które zachowują się jak typy wbudowane i cieszyć się ich zaletami.
Kiedy wywołujesz operatora New w klasie, zostanie ona przydzielona na stercie. Jednak po utworzeniu wystąpienia struktury zostanie utworzona na stosie. Przyniesie to wzrost wydajności. Ponadto nie będziesz mieć do czynienia z odwołaniami do instancji struktury, tak jak w przypadku klas. Będziesz pracować bezpośrednio z instancją struct. Z tego powodu podczas przekazywania struktury do metody jest ona przekazywana przez wartość zamiast jako odwołanie.
Więcej tutaj:
http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx
źródło
Tablice struktur są reprezentowane na stercie w ciągłym bloku pamięci, podczas gdy tablica obiektów jest reprezentowana jako ciągły blok odniesień z rzeczywistymi obiektami w innym miejscu na stercie, co wymaga pamięci zarówno dla obiektów, jak i dla ich odwołań do tablic .
W tym przypadku, gdy umieszczasz je w a
List<>
(a aList<>
jest zapisywane w tablicy), byłoby bardziej wydajne, jeśli chodzi o wykorzystanie pamięci, aby użyć struktur.(Uważaj jednak, że duże tablice trafią na stertę dużych obiektów, gdzie, jeśli ich żywotność jest długa, może mieć niekorzystny wpływ na zarządzanie pamięcią twojego procesu. Pamiętaj też, że pamięć nie jest jedynym czynnikiem).
źródło
ref
słowa kluczowego, aby sobie z tym poradzić.Jeśli mają semantykę wartości, prawdopodobnie powinieneś użyć struktury. Jeśli mają semantykę odwołań, prawdopodobnie powinieneś użyć klasy. Istnieją wyjątki, które w większości skłaniają się do tworzenia klasy, nawet jeśli istnieje semantyka wartości, ale zacznij od tego.
Jeśli chodzi o twoją drugą edycję, GC zajmuje się tylko stertą, ale jest o wiele więcej miejsca na stosie niż miejsce na stosie, więc umieszczanie rzeczy na stosie nie zawsze jest wygraną. Poza tym lista typów struktur i lista typów klas będzie na stercie w obie strony, więc nie ma to znaczenia w tym przypadku.
Edytować:
Zaczynam uważać określenie zło za szkodliwe. W końcu zrobienie mutowalnej klasy jest złym pomysłem, jeśli nie jest aktywnie potrzebne, i nie wykluczałbym kiedykolwiek używania mutowalnej struktury. Jest to kiepski pomysł tak często, że prawie zawsze jest złym pomysłem, ale przeważnie po prostu nie pokrywa się z semantyką wartości, więc po prostu nie ma sensu używać struktury w danym przypadku.
Mogą istnieć rozsądne wyjątki w przypadku prywatnych zagnieżdżonych struktur, w przypadku których wszystkie zastosowania tej struktury są zatem ograniczone do bardzo ograniczonego zakresu. Nie dotyczy to jednak tutaj.
Naprawdę myślę, że „mutuje, więc jest złym układem” nie jest dużo lepsze niż gadanie o stosie i stosie (co przynajmniej ma pewien wpływ na wydajność, nawet jeśli jest często błędnie przedstawiany). „To mutuje, więc prawdopodobnie nie ma sensu uważać go za mającego semantykę wartości, więc jest to zła struktura” jest tylko trochę inny, ale co ważne, tak myślę.
źródło
Najlepszym rozwiązaniem jest zmierzenie, ponowne zmierzenie, a następnie jeszcze większy pomiar. Mogą istnieć szczegóły tego, co robisz, co może utrudniać uproszczoną, łatwą odpowiedź, taką jak „użyj struktur” lub „użyj klas”.
źródło
Struktura jest w swej istocie niczym więcej, ani mniej niż skupiskiem pól. W .NET możliwe jest, aby struktura "udawała" obiekt, a dla każdego typu struktury .NET domyślnie definiuje typ obiektu sterty z tymi samymi polami i metodami, które - będąc obiektem sterty - będą zachowywać się jak obiekt . Zmienna, która zawiera odniesienie do takiego obiektu sterty (struktura „pudełkowa”) będzie wykazywać semantykę referencyjną, ale ta, która przechowuje bezpośrednio strukturę, jest po prostu agregacją zmiennych.
Myślę, że wiele zamieszania między strukturami a klasami wynika z faktu, że struktury mają dwa bardzo różne przypadki użycia, które powinny mieć bardzo różne wytyczne projektowe, ale wytyczne MS nie rozróżniają między nimi. Czasami istnieje potrzeba czegoś, co zachowuje się jak przedmiot; w takim przypadku wytyczne MS są całkiem rozsądne, chociaż „limit 16 bajtów” powinien prawdopodobnie być bardziej zbliżony do 24-32. Czasami jednak potrzebna jest agregacja zmiennych. Struktura używana w tym celu powinna po prostu składać się z kilku pól publicznych i prawdopodobnie pliku
Equals
przesłonięcia,ToString
przesłonięcia iIEquatable(itsType).Equals
realizacja. Struktury używane jako agregacje pól nie są obiektami i nie powinny udawać, że są. Z punktu widzenia struktury pole nie powinno oznaczać nic więcej ani mniej niż „ostatnia rzecz zapisana w tym polu”. Każde dodatkowe znaczenie powinno być określone przez kod klienta.Na przykład, jeśli struktura agregująca zmienne ma członków
Minimum
iMaximum
, sama struktura nie powinna tego obiecaćMinimum <= Maximum
. Kod, który otrzymuje taką strukturę jako parametr, powinien zachowywać się tak, jakby był przekazywany oddzielnieMinimum
iMaximum
wartości. Wymaganie, któreMinimum
nie jest większe niżMaximum
powinno być traktowane jako wymaganie, abyMinimum
parametr nie był większy niż parametr przekazywany oddzielnieMaximum
.Przydatnym wzorcem do rozważenia jest czasami
ExposedHolder<T>
zdefiniowanie klasy, na przykład:class ExposedHolder<T> { public T Value; ExposedHolder() { } ExposedHolder(T val) { Value = T; } }
Jeśli ktoś ma
List<ExposedHolder<someStruct>>
, gdziesomeStruct
jest strukturą agregującą zmienne, można zrobić takie rzeczy, jakmyList[3].Value.someField += 7;
, ale przekazaniemyList[3].Value
innemu kodowi da mu zawartość,Value
a nie da mu możliwości zmiany. Z drugiej strony, gdyby ktoś użył aList<someStruct>
, należałoby użyćvar temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Gdyby ktoś użył zmiennego typu klasy, ujawnienie zawartościmyList[3]
kodu z zewnątrz wymagałoby skopiowania wszystkich pól do innego obiektu. Gdyby ktoś użył niezmiennego typu klasy lub struktury „w stylu obiektowym”, należałoby skonstruować nową instancję, która byłaby podobna,myList[3]
zsomeField
tą różnicą, że była inna, a następnie zapisać tę nową instancję na liście.Jedna dodatkowa uwaga: jeśli przechowujesz dużą liczbę podobnych rzeczy, dobrym rozwiązaniem może być przechowywanie ich w potencjalnie zagnieżdżonych tablicach struktur, najlepiej starając się zachować rozmiar każdej tablicy w przedziale od 1 KB do 64 KB lub więcej. Tablice struktur są szczególne, ponieważ indeksowanie da bezpośrednie odniesienie do struktury wewnątrz, więc można powiedzieć „a [12] .x = 5;”. Chociaż można zdefiniować obiekty podobne do tablic, C # nie pozwala im na współdzielenie takiej składni z tablicami.
źródło
Użyj klas.
Ogólnie rzecz biorąc. Dlaczego nie zaktualizować wartości b podczas ich tworzenia?
źródło
Z punktu widzenia c ++ zgadzam się, że modyfikowanie właściwości struktury będzie wolniejsze w porównaniu z klasą. Ale myślę, że odczytanie ich będzie szybsze, ponieważ struktura zostanie przydzielona na stosie zamiast na stercie. Odczytywanie danych ze stosu wymaga większej liczby sprawdzeń niż ze stosu.
źródło
Cóż, jeśli wybierzesz struct afterall, pozbądź się stringów i użyj buforu char lub bajt o stałym rozmiarze.
To jest re: wydajność.
źródło