Dlaczego niestandardowe zarządzanie pamięcią jest potrzebne dla zasobów?

10

Niemal wszystkie zasoby związane z programowaniem gier, zwłaszcza trójwymiarowe gry otwartego świata, mówią o tym, jak trzeba stale rozładowywać i ponownie ładować zasoby na dysk iz pamięci systemowej i wideo. Rozumiem to na konsolach, ponieważ mają one bardzo proste schematy zarządzania pamięcią, które nie są w stanie poradzić sobie z potencjalnym przepełnieniem.

Jednak na PC sytuacja wygląda zupełnie inaczej. System operacyjny udostępnia pamięć wirtualną dla pamięci systemowej, a sterownik karty graficznej obsługuje zamianę procesora do procesora graficznego. Dlaczego więc nie wystarczy załadować wszystkiego naraz i trzeba tylko poradzić sobie z pobieraniem wstępnym i być może upewnić się, że nie ma blokowania podczas zamiany wymaganego zasobu?


Nie oznacza to, że wszystko jest przydzielane naiwnie za pomocą czegoś podobnego malloc. Wszystko pozostanie ciągłe i spójne z pamięcią podręczną. Zapewne łatwiej jest zwiększyć efektywność dostępu do pamięci, jeśli nie musisz się martwić o ręczne wczytywanie i wyprowadzanie ...


AKTUALIZACJA:

Po przeczytaniu trochę asynchronicznych transferów buforów , wpadłem na pomysł, że sposób, w jaki sterownik karty graficznej może automatycznie wczytywać i wyjmować pamięć, jest nieoptymalny. Czy dotyczy to również zasobów procesora; tzn. czy lepiej jest zawsze ręcznie zarządzać, kiedy dany zasób ma być ładowany i rozładowywany, nawet w obecności pamięci wirtualnej?

jmegaffin
źródło
1
Zakładasz, że zawartość zasobu jest przechowywana w pamięci systemowej. Wiele danych zasobów będzie przechowywanych w pamięci GPU, która jest znacznie bardziej ograniczona.
dadoo Games
Po przeczytaniu tutaj odpowiedzi mam pomysł, że sterowniki OpenGL automatycznie przechowują również wszystkie bufory / tekstury na procesorze. Widzę jednak, dlaczego warto ręcznie określić te transfery, aby skorzystać z przesyłania asynchronicznego .
jmegaffin
Czy jesteś pewien, że ma to znaczenie dla twojego projektu gry? Nie jestem pewien, czy widzę techniczne wyzwanie, tylko rozwiązanie. Jeśli nie stoisz przed wyzwaniem technicznym, trudno jest dopasować rozwiązanie lub je zrozumieć.
AturSams,
Zastanawiam się, dlaczego silniki gier wdrażają złożone systemy buforowania zasobów, gdy systemy / sterowniki mają już dostępne systemy pamięci wirtualnej. Jest to dla mnie istotne, ponieważ piszę grę w otwartym świecie, w której ta informacja jest bardzo ważna.
jmegaffin
Czy Twoja gra ma problemy z wydajnością, a narzędzie do profilowania pokazuje, że jest spowodowane dostępem do pamięci? Problem z grami, które zawierają poziomy otwarte, polega na tym, że pod względem technicznym przekłada się to na wiele danych. Im większa kontrola nad tymi danymi, tym lepsza będzie wydajność. Dlaczego nie wdrożyć najbardziej wydajnego systemu do zarządzania danymi? Rozwój gry jest bardzo konkurencyjny, a kiedy masz pieniądze na ekspertów, nie pozostawiasz kamienia odwróconego, starając się wycisnąć więcej szczegółów i wyższą liczbę klatek na sekundę.
AturSams,

Odpowiedzi:

16

Nowoczesne gry z otwartym światem po prostu nie mieszczą się w pamięci. Należy pamiętać, że większość gier jest nadal 32-bitowa ze względu na liczbę graczy z 32-bitowymi systemami operacyjnymi, a proces 32-bitowy może mieć co najwyżej tylko 4 GB zaadresowanej pamięci (niezależnie od pamięci wirtualnej), ale realistycznie ograniczona do 2-3 GB. Rzut fragmentacji i faktyczna ilość użytecznych obiektów, które można mieć jednocześnie w pamięci, jest nieco mniejsza niż całkowita ilość danych potrzebnych w dużym środowisku. Nawet w 64-bitowym systemie operacyjnym często trzeba atakować maszyny o niskiej specyfikacji z tylko 4 GB pamięci, aby zapewnić maksymalną kompatybilność dla użytkownika końcowego. Nie zapominajmy o urządzeniach mobilnych, sprzęcie o „niedostatecznym zasilaniu”, takim jak WiiU itp. Programiści muszą skupiać się na czymś więcej niż tylko na dużych, fantazyjnych platformach do gier.

Pamięć wirtualna nie jest rozwiązaniem, po pierwsze dlatego, że nie neguje ograniczeń przestrzeni adresowej, a także dlatego, że nie jest optymalna. System operacyjny może wyświetlać strony w nieodpowiednich momentach lub w nieodpowiedni sposób. Był to problem, którym zajmowały się systemy operacyjne, gdy po raz pierwszy pojawiła się hibernacja; stronicowanie procesów na dysk, a następnie ponowne ich wczytywanie było w niektórych przypadkach znacznie wolniejsze niż w przypadku jawnego przesyłania strumieniowego zasobów z powrotem. Problemy nadal występują, gdy w systemie zaczyna brakować pamięci, strony są stronicowane na dysk, a system przestaje odpowiadać, staje się krytycznym elementem niezbędnym do uruchomienia WM / explorer.exe. Pamięć wirtualna była rozwiązaniem opracowanym dla problemu sprzed dziesięcioleci, w którym systemy miały bardzo mało pamięci RAM, a różnica między pamięcią a szybkością dysku była znacznie mniejsza. Nawet dzisiaj' Najszybsze dyski SSD to niewielki ułamek prędkości taniej / wolnej pamięci RAM, a próba udawania, że ​​możesz używać dysku jako wirtualnej pamięci RAM, nie jest już najlepszym pomysłem. Jest to wiek, w którym niektóre osoby robią coś przeciwnego i ładują systemy plików na dyski RAM; ograniczenia, które doprowadziły do ​​utworzenia pamięci wirtualnej, nie są już prawdziwe.

Niezależnie od problemu z dyskiem, niestandardowe zarządzanie zasobami jest nadal dużą częścią rozwoju gier. Od debugowania haków po kwestie dotyczące fragmentacji po inteligentne przesyłanie złożonych zasobów - udawanie, że cały świat jest nieskończenie dużym pudełkiem bitów, nie jest realistyczne.

Sean Middleditch
źródło
Dziękuję za szczegółową odpowiedź. Chociaż wciąż szukam informacji o sposobie, w jaki sterowniki GPU obsługują pamięć wirtualną (między VRAM a systemową pamięcią RAM).
jmegaffin
@Boreal: te same problemy. Po prostu zamień „RAM” na „VRAM”, a „dysk” na „pamięć procesora
Sean Middleditch,
@SeanMiddleditch +1 Dobra odpowiedź: „To jest wiek, w którym niektórzy robią coś przeciwnego i ładują systemy plików na dyski RAM”. Czy masz na myśli pliki odwzorowane w pamięci? Rozumiem również z twojej odpowiedzi, że niestandardowy alokator pamięci uniemożliwia systemowi stronicowania pamięci, czy nie jest tak, ponieważ pamięć będzie stronicowana, o ile poprosisz o pamięć z jądra za pomocą brk (), na przykład, w jaki sposób stronicowanie na PC jest dokładnie zapobiegane przy użyciu niestandardowego przydziału pamięci?
concept3d
1
@ concept3d: nie mapowanie pamięci, ale dosłowne dyski RAM. Możesz utworzyć dysk, który jest zabezpieczony pamięcią, a nie dyskiem. Niestandardowy menedżer pamięci nie zapobiega stronicowaniu (niektóre inne aplikacje mogą zużywać całą pamięć). Inteligentne zarządzanie zasobami (w warstwie zarządzania obiektami / zasobami) oznacza po prostu, że zwalniasz pamięć dla obiektów, których już nie potrzebujesz. Pomyśl o pamięciach dyskowych systemu operacyjnego, które przechowuje w pamięci
Sean Middleditch
6

W przypadku gry 32-bitowej, ponieważ większość gier jest z różnych powodów, nawet gra na jednej jednostronnej płycie DVD (4,3 GB) ma już znacznie więcej treści, które można zmieścić w 32-bitowej przestrzeni adresowej. I przy założeniu, że zawartość nie jest skompresowana na dysku, i idealnie optymalnie, załaduj wszystko naraz do ciągłego podejścia do przestrzeni adresowej. Wiele gier jest teraz dostępnych na wielu dyskach DVD, łatwo przekraczając 10 GB zawartości. Z mojego doświadczenia na PC, gdy twoja przestrzeń adresowa zbliży się, przydzielanie 2 GB zacznie się nie powieść, a to stanie się twardym ograniczeniem, niezależnie od ilości pamięci fizycznej użytkownika.

Jeśli wykonasz skok do wersji 64-bitowej, zakładając, że Twój silnik ją obsługuje, ten twardy limit znika, ale zostaje zastąpiony efektywnym miękkim limitem - gdy tylko zacznie się zamiana pamięci wirtualnej, wydajność gry spadnie niedopuszczalnie. Jeśli główny proces kiedykolwiek będzie musiał czekać na ponowne zapisanie pamięci, liczba klatek na sekundę będzie się zacinać i ulegać usterce. Pamięć wirtualna może uczynić tę przejrzystą, ale nie czyni jej wydajną. Fizyczne odzyskanie pamięci z dysku zajmuje trochę czasu, a podczas oczekiwania gra się zatrzymała. Chociaż można się obejść (z pobieraniem w tle stron pamięci, o których wiesz, że będziesz wkrótce potrzebować), jest to trudny i niewiarygodny proces, który nadal będzie miał wpływ na wydajność.

Algorytmy systemu operacyjnego służące do decydowania, które strony zamienić na dysk, nie mają wiedzy na temat gry i tego, czego może potrzebować w przyszłości. Ponadto system operacyjny nadal musi obsługiwać inne aplikacje działające jednocześnie z grą, a także sam system operacyjny. Konieczność utknięcia w miejscu, gdy strony są zwracane z dysku, jest ogromnym spadkiem wydajności i nie ma znaczenia, czy to twoja gra, inna aplikacja lub system operacyjny, który ją poniesie, wydajność gry znacznie ucierpi.

Niezależnie od tego, czy limit pamięci jest miękki czy twardy, pamięć jest nadal ograniczona. Ale nawet jeśli możesz po prostu załadować wszystko naraz, nie przekraczając limitu wydajności, wciąż istnieją powody, dla których nie chciałbyś:

  1. Wczytanie gry i przejście do stanu interaktywnego trwa znacznie dłużej, niż jest to konieczne, ponieważ po prostu ładuje wszystko na początku i utrzymuje w pamięci. Z dysku twardego można załadować około 150 MB / s, a nawet najszybszy napęd DVD (24x) ładuje z prędkością 33 MB / s. Oznacza to, że przykładowa zawartość DVD o pojemności 4,3 GB zajmie co najmniej 30 sekund, aby załadować ją z dysku twardego i ponad 133 sekundy, aby załadować ją z dysku DVD. Są to niedopuszczalnie długie czasy ładowania.

  2. Twoja gra zajmuje dużo więcej pamięci, niż jest to uzasadnione. Jeśli tylko 10% zawartości jest jednocześnie widoczne, przytrzymanie reszty rezydenta jest po prostu marnotrawstwem i uniemożliwia użytkownikowi użycie tej pamięci do czegoś innego. Użytkownicy zazwyczaj nie wybaczają gier, które wymagają nieproporcjonalnie dużej ilości pamięci do prawidłowego działania.

Dynamiczne przesyłanie strumieniowe zasobów z dysku, zarówno w tle, jak i synchronicznie podczas ładowania ekranu, pozwala znacznie zwiększyć efektywność wykorzystania pamięci, przy niewielkim wysiłku i jedynie niewielkim minusem dla użytkownika. Ale jest to optymalizacja i jak każda inna optymalizacja ma malejące zwroty, im więcej próbujesz zastosować. W pewnym momencie wzrost powierzchni pamięci dla utrzymania rezydenta jest przeważony nad korzyściami wynikającymi z posiadania go bez konieczności ładowania. Ale dopóki ten punkt nie zostanie osiągnięty, lepiej jest ładować tylko to, czego potrzebujesz, kiedy jest to potrzebne.

MrCranky
źródło
1

Mówiąc bardzo ogólnie i upraszczająco, i bez uwzględnienia szczegółów implementacji pamięci wirtualnej, deweloper zawsze ma wiedzę na temat przewidywania, której brakuje implementacji maszyny wirtualnej.

Deweloper zawsze może powiedzieć: „Nie muszę teraz ładować tego pliku audio. Muzyka w środku jest używana tylko w grze na ekranie”. I zaraz po zakończeniu gry na ekranie programista może powiedzieć: „Nie potrzebuję już tego klipu audio w pamięci fizycznej. Jest on używany tylko w tej grze na ekranie”.

System operacyjny nie ma takiego przewidywania. Być może uda się ustalić, wiele błędów strony później, że jakiś klip audio nie jest już potrzebny w pamięci fizycznej, ponieważ nie był dostępny od dłuższego czasu. Ale przekształcenie foresightu w spojrzenie z perspektywy czasu przekłada się na wiele błędów stron, a wiele błędów stron przekłada się na czkawkę w liczbie klatek na sekundę w oprogramowaniu tak krytycznym czasowo jak gra wideo. Tam dalekowzroczność dewelopera naprawdę pomaga, jeśli chcesz uniknąć takich czkawek.

I dotyczy to koncepcyjnie niezależnie od sprzętu i oprogramowania. Zakładając, że stronicowanie w pamięci jest kosztowne, to przewidywanie programisty zawsze pomoże w zmniejszeniu tego kosztu.

Mówiąc jeszcze szerzej, między projektantem sprzętu, projektantem kompilatora, projektantem systemu operacyjnego / sterowników i twórcą aplikacji jest niekończący się cykl. Twórcy sprzętu / kompilatora / systemu operacyjnego / sterownika często próbują wdrożyć optymalizacje w celu przyspieszenia przeciętnej aplikacji w oparciu o zwykłe wzorce dostępu do pamięci, a czasem może z nadzieją: „Ludzie powinni po prostu móc pisać kod, jak chcą i to powinno być szybkie ”.Ale jeśli pojawi się jakakolwiek myśl o tym typie, zwykle nie udaje się to w przypadku pól krytycznych pod względem wydajności, ponieważ wtedy programiści o krytycznej wydajności zaczynają poznawać skomplikowane szczegóły swojego kompilatora, sprzętu, systemu operacyjnego, sterowników itp. I zaczynają pisać kod zaprojektowane tak, aby wykorzystać to w jak największym stopniu, aby napisać najszybszy możliwy kod (takie jak prefiksy, dzielenie pola gorącego / zimnego, SoAs itp. dla kodu przyjaznego dla pamięci podręcznej). I to jest jak gra, która nigdy się nie kończy. Te rzeczy nigdy nie są traktowane jako czarne skrzynki w polach krytycznych pod względem wydajności, ponieważ programiści konkurują o wydajność.

Osobiście chciałbym, żeby pamięć wirtualna nie istniała, ponieważ dodaje ona dodatkową warstwę nieprzewidywalności wydajności w sposób, który bywa zbyt ekstremalny i niesie ze sobą zbyt dużą utratę wydajności, gdy rzeczy idą naprawdę na południe do punktu bezużyteczności. Czasami natrafiam na przypadki, w których korzystałem z aplikacji, w której przypadkowo wpisałem dodatkową cyfrę lub dwie, gdy byłem pijany w jakimś polu wejściowym, co spowodowało, że wyczerpało ono pamięć fizyczną tak szybko, że doprowadziło system operacyjny do pełzania, gdzie nie mogłem t nawet kliknąłem przycisk Anuluj na pasku postępu i musiałem czekać 10 minut, jednocześnie wciskając Ctrl + Alt + Del, aby zabić proces i przeklinać siebie podczas rozlewania drinka, aby wrócić do punktu, w którym rzeczy są znowu użyteczne, i że pomimo zapisania pliku strony na dysku SSD. W takich przypadkach wolałbym błąd „brak pamięci” lub coś takiego, w którym to momencie mógłbym zamknąć niektóre z moich 17 zakładek porno (w porządku, i tak dodaję do ulubionych), aby zwolnić trochę pamięci, a następnie natychmiast przejść wznowienie mojej działalności.


źródło