Wydajność RAII w gamedevie.

Wydajność RAII w gamedevie.
OS
  • Rejestracja:7 miesięcy
  • Ostatnio:około godziny
  • Postów:10
0

Z wiki. https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

At the 2018 Gamelab conference, Jonathan Blow explained how use of RAII can cause memory fragmentation which in turn can cause cache misses and a 100 times or worse hit on performance.

Czy te spadek wydajności, o którym mowa jest chwilowy? Coś jak GC w javie? Obecnie staram się używać shared_ptr choć zdaję sobie sprawę, że to nie jest super wydajne rozwiązanie. Trzeba być naprawdę kozakiem, żeby sobie bez RAII zarządzać alokowaniem pamięci. Nawet nie potrafię sobie tego wyobrazić w game loopie.

AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około 11 godzin
1

Największą wpływ na wydajność ma samo tworzenie i likwidowanie obiektów, zwłaszcza przydzielanie i zwalnianie zasobów związanych z obiektem. Moim zdaniem, spadek wydajności jest właśnie chwilowy, w chwili uzyskiwania dostępu do zasobu. Jeżeli na przykład z obiektem związany jest plik, to nawet, jak nie stosuje się RAII, to i tak prędzej czy później jest potrzebne otwarcie pliku.

Metoda tworzenia i likwidowania obiektu (tradycyjne new/delete lub shared_ptr) teoretycznie też ma wpływ na wydajność, ale w większości przypadków ten wpływ jest pomijanie mały, a użycie shared_ptr lub unique_ptr znacznie zmniejsza ryzyko najczęstszych błędów związanych z zarządzaniem pamięcią, które zarazem bywają trudne do stwierdzenia.

Dużo zależy od tego, co tworzysz, ale moim zdaniem, o ile jest taka możliwość, najlepiej będzie ograniczyć tworzenie i likwidowanie obiektów do minimum, zamiast tego np. użyć wielokrotnie tego samego obiektu.

Na przykład, jeżeli co chwilę tworzysz i likwidujesz obiekt i nie możesz kontrolować liczby obiektów danej klasy, to można zrobić pulę obiektów.

edytowany 1x, ostatnio: andrzejlisek
SL
  • Rejestracja:około 7 lat
  • Ostatnio:8 minut
  • Postów:866
1

https://en.wikipedia.org/wiki/AoS_and_SoA . RAII zachęca do AoS. Samo w sobie RAII nie jest złe czy wolne: po prostu dzięki wygodzie możesz wybrać podejście AoS, gdzie bez tej wygody być może zdecydowałbyś się na SoA. Tyle

Trzeba być naprawdę kozakiem, żeby sobie bez RAII zarządzać alokowaniem pamięci. Nawet nie potrafię sobie tego wyobrazić w game loopie.

Zamiast alokacji wrzucasz wszystko do vectora. Proste jak drut

edytowany 1x, ostatnio: slsy
several
  • Rejestracja:ponad 15 lat
  • Ostatnio:około 11 godzin
4

At the 2018 Gamelab conference, Jonathan Blow explained how use of RAII can cause memory fragmentation which in turn can cause cache misses and a 100 times or worse hit on performance

RAII jako technika samoa w sobie nie powoduje fragmentacji pamięci. Sytuacja robi się jaśniejsza gdy ma się nieco więcej kontekstu w postaci szerszych i nieco luźniejszych wypowiedzi Jonathana Blowa. To co konkretnie ma na myśli to to częste wołanie globalnego new/delete albo malloc/free co może faktycznie powodować fragmentacje, a RAII dodatkowo może zachęcać do takiej złej praktyki dając dodatkowe bezpieczeństwo.

To o czym Jonathan Blow nie wspomina to fakt, że RAII nie jest równoznaczne z wołaniem new/delete w zakresie, a równie dobrze może operować na alokatorach czy arenie pamięci jaką sobie przygotowałeś. Także wytykanie palcem RAII to bardziej powieszchowny skrót myślowy, gdzie faktycznym problemem jest ciągła alokacja i dealokacja na stercie dla małych pierdół.

(edit)

Trzeba być naprawdę kozakiem, żeby sobie bez RAII zarządzać alokowaniem pamięci

Szczerze napisawszy, to jak sobie dobrze poukładasz arenę, to RAII naturalnie przestaje byc potrzebne, szczególnie przy robieniu gierek, gdzie jesteś w stanie sporo przewidzieć bo duża część danych jest pod Twoją kontrolą. Tym nie mniej, i tak co jakiś czas możesz potrzebować scratch memory np. do jakś danych do debugowania i RAII cały czas może być użyteczne.

A jeśli codzi o Jonathana Blowa to zapewniam Cię, że też RAII używał/używa np. do operowania muteksami.


edytowany 3x, ostatnio: several
O2
A jak to wygląda od strony implementacji new/malloc w jakiś popularnych platformach ? Bo ja to zawsze rozumiałem tak że fizycznie system przydziela jakiś tam obszar pamięci i dopóki nie zachodzi potrzeba jego powiększenia to działa to trochę jak memory pool ? W takim wypadku raczej niekoniecznie będziemy mieli fragmentację.
several
Pytasz o przydział wirtualnej pamięci do procesu? I co się kryje pod popularne platformy? Jeśli mówimy o PC to problem faktycznie może być mniejszy, taki gcc używa aren i free list od dawna, tym nie mniej ludzie nadal implementują te techniki po swojej stronie żeby zachować kontrolę po swojej stronie nad kontaktem procesu z systemem by móc to ograniczać w razie potrzeby. W gamedev programują też na konsole i handheldy i wciąż temat fragmentacji wraca, ale na tych platformach to się nie znam.
SL
@Oggy2: alokatory generalnie starają się przyśpieszać alokację mały obiektów, bo przy tych dużych i tak narzut to system operacyjny, którego nie poprawisz. Kluczem jest alokowanie pamięci raz i jej reużycie. Częstym trikiem są np. free listy (po zwolnieniu obiekt jest wrzucany do free listy z której korzystają kolejne alokacje) i/albo size classy (przy alokacji obiektu o rozmiarze 23 najlepiej użyć klasy obiektów dla przykładowo rozmiaru 32 zamiast rozdrabniać się). Do tego trzeba dodać dobrą współbieżność, bo tradycyjnie alokatory bardzo słabo sobie z tym radziły
SL
  • Rejestracja:około 7 lat
  • Ostatnio:8 minut
  • Postów:866
1

Trzeba być naprawdę kozakiem, żeby sobie bez RAII zarządzać alokowaniem pamięci. Nawet nie potrafię sobie tego wyobrazić w game loopie.

Dodam jeszcze, że ważna jest optymalizacja kluczowego kodu. Przykładowo jak gra w kółko oblicza skomplikowane AI to warto, żeby wszystkie obiekty odpowiedzialne za te obliczenia (stan mapy, wszyscy aktorzy) byli zaprojektowani w taki sposób, żeby używać tej samej pamięci zamiast co chwile robić nowe obiekty. Layout (taki jak SoA) też się przydaje. Patterny takie jak ECS https://en.wikipedia.org/wiki/Entity_component_system są znamy sposobem na projektowanie gry w taki sposób, żeby było wydajnie i czytelnie

Oczywiście jak masz mało kluczowy kod np. coś co się wykonuje raz na parę sekund np. przyszła wiadomość z chatu albo gracz coś craftuje to nie musisz się spinać, bo kluczem jest optymalizacja tego co się dzieje co klatkę, bo jest ich po prostu dużo

edytowany 1x, ostatnio: slsy
GO
  • Rejestracja:11 miesięcy
  • Ostatnio:4 miesiące
  • Postów:358
1

C++ jest niskopoziomowy to możesz w takim vektorze objekty przechowywać lub wskaźniki na obiekty, co wtedy w drugim przypadku każdy obiekt będzie gdzieś indziej bo tak allokator mógł przydzielić, a w pierwszym przypadku będzie jeden blok pamięci continoius gdzie będą wszystkie obiekty.
Są plusy i minus, gorzej jest dodawać nowe elementy, chyba że z zapasem się zarezerwuje, usuwanie też jest utrudnione, a przy pointerach jest łatwiej bo jak usuniesz to łatwiej przesunąć pamięć.

Cache jest też set associative w cpu i adres zależy od setu w jakim się znajdzie i może być do przykładowo 8 elementów cache w jednym secie więc dopiero jak się wypchnie poprzednie dane z cache to następne odwołanie pod ten sam adres będzie wolniejsze, stosuje to się przy atakachach side channel gdzie celowo nadpisujesz cały cache i potem porównując czas dostępu możesz przewidzieć do czego uzyskał inny proces dostęp, gdyż on ponownie sprawi, że dany adres się załaduje do cache przez użycie tego.
Albo np. w starych atakach robiło się instrukcję, która powodowała crash, ale ty kontrolowałeś tablicę i index pointera, gdzie indexem była wartość kernela, gdzie procesor wykonując obliczenia do przodu i w razie błędu rollback to załadowywał strony cache, które można było badać, której tablicy był każdy element wielkości cache line i jak kernel dostał seqfault to ty badając który element tablicy ma szybki dostęp mogłeś przewidzieć jaki jest bajt w pamięci kernela.
Działało to na zasadzie branch prediction gdzie procesor miał rozgałęzienia i błędnie przewidział co ma się wykonać wywoływał ahead wyciągnięcie bajta z pamięci i użycie go do dostępu do tablicy, co skutkowało załadowaniem tego elementu, który był o wielkości cache line do cache i potem był rollback lub crash i drugi program mający dostęp do tego regionu sobie badał, ile czasu zajmuje dostęp do każdego elementu i ten, który był najkrótszy to był bajt, który znajdował się w pamięci kernela.

Przy continous memory, cache jak ładuje jeden cache line to jest optymalizacja, która ładuje automatycznie też następny obok cache line, więc jak dane leżą obok siebie to jak korzystasz z jednego fragmentu to już procesor ładuje następny obok, jak przejdziesz na następny załadowany to ładuje w tym czasie już następny.
Ogólnie procesor ma wiele funkcji przewidywania, nawet nie wszystkie są udokumentowane bo są na poziomie hardware, w apple było pełne błędów, gdzie każdy pointer jaki występował w danym cache linie, który procesor właśnie wykonywał instrukcje to z góry ładowało wszystkie adresy do pamięci i to było jednym z kluczowych elementów side channel attaku na tą platformę, ale jakoś ją potem zpatchowali.

edytowany 2x, ostatnio: .GodOfCode.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)