Obecnie jestem na etapie projektowania architektury opartej na komponentach w C ++.
Mój obecny projekt obejmuje wykorzystanie takich funkcji, jak:
std::vector
sstd::shared_ptr
do trzymania komponentówstd::dynamic_pointer_cast
std::unordered_map<std::string,[yada]>
Komponenty będą reprezentować dane i logikę różnych elementów potrzebnych w oprogramowaniu podobnym do gry, takim jak grafika, fizyka, sztuczna inteligencja, audio itp.
Wszędzie czytałem, że brakujące pamięci podręczne mają duży wpływ na wydajność, więc przeprowadziłem kilka testów, które doprowadziły mnie do wniosku, że może to spowolnić działanie aplikacji.
Nie byłem w stanie przetestować wyżej wymienionych funkcji językowych, ale w wielu miejscach mówi się, że kosztują one dużo i w miarę możliwości należy ich unikać.
Ponieważ jestem na etapie projektowania architektury i zostaną one uwzględnione w rdzeniu projektu, czy powinienem teraz spróbować znaleźć sposoby na ich uniknięcie, ponieważ będzie bardzo trudno zmienić to później, jeśli będzie wydajność problemy?
A może po prostu przyłapałem się na przedwczesnej optymalizacji?
źródło
the answer to this question is almost always a resounding "YES !!"
. Nadal uważam, że lepiej jest najpierw uruchomić i zoptymalizować później, ale YMMV, każdy ma swoje zdanie, z których wszystkie są ważne i tylko OP może naprawdę odpowiedzieć na swoje - subiektywne - pytanie.Odpowiedzi:
Bez czytania niczego poza tytułem: Tak.
Po przeczytaniu tekstu: Tak. Chociaż prawdą jest, że mapy i wspólne wskaźniki itp. Nie działają dobrze pod względem pamięci podręcznej, z pewnością okaże się, że to, co chcesz z nich korzystać - o ile rozumiem - nie stanowi wąskiego gardła i nie będzie trzymane w efektywnie wykorzystuj pamięć podręczną niezależnie od struktury danych.
Napisz oprogramowanie, unikając najgłupszych błędów, a następnie przetestuj, a następnie znajdź wąskie gardła i zoptymalizuj!
Fwiw: https://xkcd.com/1691/
źródło
Nie znam C ++, ale ogólnie Zależy.
Nie trzeba przedwcześnie optymalizować izolowanych algorytmów, w których można łatwo zoptymalizować, jeśli chodzi o to.
Jednak musisz uzyskać ogólny projekt aplikacji, aby osiągnąć pożądane kluczowe wskaźniki wydajności.
Na przykład, jeśli chcesz zaprojektować aplikację do obsługi milionów żądań na sekundę, musisz pomyśleć o skalowalności aplikacji podczas jej projektowania, a nie o uruchomieniu aplikacji.
źródło
Jeśli musisz zapytać, to tak. Przedwczesna optymalizacja oznacza optymalizację, zanim masz pewność, że występuje poważny problem z wydajnością.
źródło
ECS? Właściwie zasugeruję, że może to nie być przedwczesne, jeśli tak, aby przemyśleć stronę projektu zorientowaną na dane i porównać różne powtórzenia, ponieważ może to wpłynąć na projekty interfejsów , a ta ostatnia jest bardzo kosztowna, aby zmienić ją późno gra. Również ECS wymaga dużo pracy i przemyślenia z wyprzedzeniem i myślę, że warto poświęcić trochę tego czasu, aby upewnić się, że nie przyniesie ci to żalu wydajności na poziomie projektowym, biorąc pod uwagę, jak będzie w sercu twojego cały cholerny silnik. Ta część rzuca mi spojrzenie:
Nawet przy małych optymalizacjach ciągów kontener o zmiennej wielkości (łańcuchy) znajduje się w innym kontenerze o zmiennej wielkości (unordered_maps). W rzeczywistości, małe optymalizacje strunowe faktycznie mogła być równie szkodliwe jak pomocne w tym przypadku, jeśli stół jest bardzo rzadki, ponieważ małe optymalizacja ciąg oznaczałoby, że każdy niewykorzystany indeks tabeli mieszania będzie nadal korzystać z większej ilości pamięci dla optymalizacji SS (
sizeof(string)
będzie być znacznie większy) do tego stopnia, że całkowity narzut pamięci w tabeli skrótów może kosztować więcej niż cokolwiek, co jest w niej przechowywane, szczególnie jeśli jest to prosty komponent, taki jak element pozycji, oprócz ponoszenia większej ilości braków w pamięci podręcznej z ogromnym krokiem aby przejść od jednego wpisu w tabeli skrótów do następnego.Zakładam, że ciąg jest jakimś kluczem, takim jak identyfikator komponentu. Jeśli tak, to już czyni rzeczy znacznie tańszymi:
... jeśli chcesz mieć korzyści z posiadania przyjaznych dla użytkownika nazw, których mogą używać skrypty, np. wtedy wewnętrzne łańcuchy mogą dać ci to, co najlepsze z obu światów.
To powiedziawszy, jeśli możesz odwzorować ciąg na stosunkowo niski zakres gęsto używanych wskaźników, możesz po prostu być w stanie to zrobić:
Powodem, dla którego nie uważam tego przedwczesnego, jest to, że znowu może to wpłynąć na projekty interfejsów. Celem DOD nie powinna być próba wymyślenia najbardziej wydajnych reprezentacji danych możliwych do wyobrażenia za jednym razem IMO (które ogólnie powinny być osiągane iteracyjnie w razie potrzeby), ale przemyślenie ich na tyle, aby zaprojektować interfejsy do pracy z tym dane, które zapewniają wystarczającą swobodę oddechu do profilowania i optymalizacji bez kaskadowych zmian w projekcie.
Naiwnym przykładem jest oprogramowanie do przetwarzania wideo, które łączy cały swój kod z tym:
Nie zajdzie daleko bez potencjalnie epickiego przepisywania, ponieważ pomysł abstrakcji na poziomie pojedynczego piksela jest już niezwykle nieefektywny (
vptr
sam kosztowałby często więcej pamięci niż cały piksel) w porównaniu do abstrakcji na poziomie obrazu (który będzie często reprezentują miliony pikseli). Dlatego z góry zastanów się nad przedstawieniami danych, abyś nie musiał stawić czoła takiemu koszmarnemu scenariuszowi, a najlepiej nie więcej, ale myślę, że warto o tym pomyśleć z góry, ponieważ nie chcesz budować skomplikowany silnik wokół ECS i przekonaj się, że sam ECS stanowi wąskie gardło w zakresie, który wymaga zmiany rzeczy na poziomie projektu.Jeśli chodzi o pomyłki w pamięci podręcznej ECS, moim zdaniem programiści często starają się, aby ich ECS był przyjazny dla pamięci podręcznej. Zaczyna dawać zbyt mały huk, aby złotówka próbowała uzyskać dostęp do wszystkich komponentów w idealnie ciągły sposób, i często oznacza kopiowanie i tasowanie danych w dowolnym miejscu. Zwykle wystarczy, powiedzmy, po prostu posortować indeksy składników sortowania przed uzyskaniem dostępu do nich, aby uzyskać do nich dostęp w sposób, w którym przynajmniej nie ładujesz obszaru pamięci do linii pamięci podręcznej, tylko aby go eksmitować, a następnie załadować wszystko od nowa w tej samej pętli, aby uzyskać dostęp do innej części tej samej linii pamięci podręcznej. I ECS nie musi zapewniać niesamowitej wydajności na całym pokładzie. To nie jest tak, że system wejściowy czerpie z tego tyle korzyści, co fizyka lub system renderowania, więc zalecam dążenie do „dobrego” ogólna wydajność i „doskonała” w miejscach, w których naprawdę jej potrzebujesz. To powiedziawszy, użyj
unordered_map
istring
tutaj są wystarczająco łatwe do uniknięcia.źródło