Kiedy najlepiej rozważyć wydajność?

45

Pochodzę ze środowiska programistycznego. Podczas cyklu tworzenia oprogramowania zwykle koncentrujemy się na funkcjonalnościach i działającym produkcie. Pod koniec programowania zaczynamy optymalizować kod i zwiększać wydajność.

Teraz pytanie brzmi: czy musimy myśleć o wydajności w każdym wierszu kodu przy tworzeniu gier?

W ten sposób czuję, że brakuje nam wzorów i czystego kodu. Zastanawiałem się, czy istnieje jakakolwiek najlepsza praktyka w branży tworzenia gier?

Emad
źródło
8
Dyskusja na temat poprzedniego pytania na temat przedwczesnej optymalizacji może okazać się przydatna.
DMGregory
10
Na początku. W miarę postępu projektu wydajność będzie się pogarszać.
Tomáš Zato
10
Pochodzenie programisty, z którego pochodzisz, nie brzmi zdrowo. Projekty muszą uwzględniać kwestie wydajności we wszystkich dziedzinach. Ile okrążeń w sieci, ile i jak dużo zapytań do bazy danych, ile pamięci i tak dalej. Nie ma znaczenia, w jakiej dziedzinie inżynierii to robisz; jeśli nie planujesz wydajności, planujesz jej brak.
Jon Watte
1
Wszystko. The. Czas. Zaoszczędź sobie trochę czasu i nudnych zadań, po prostu wykonując cały czas, a zobaczysz, że wykonujesz bardzo fajne i geekiczne zadania, próbując dalej optymalizować kod, niż jest już (jeśli to konieczne). Tam zaczyna się zabawa.
Yates
6
„Rozważenie” wydajności powinno nastąpić w dniu, w którym zdecydujesz się stworzyć produkt, który ma użytkowników . Użytkownicy mają minimalne oczekiwania dotyczące wydajności, a jeśli nie zostaną spełnione, produkt zawiedzie. Wydajność ma koszty, a produkty mają budżety . Jeśli minimalna akceptowalna wydajność kosztuje więcej niż budżet, projekt się nie powiedzie . Rozsądnie jest myśleć o tym pierwszego dnia, a nie pierwszego.
Eric Lippert,

Odpowiedzi:

90

Inżynieria wydajności

  • Postępuj zgodnie z zaleceniami dostawcy.
  • Użyj poprawnych struktur danych.
  • Zaimplementuj prawidłowe wzorce użytkowania.
  • Nie rób nic głupiego.

Optymalizacja

  • Gdy już napisany kod działa wolno, zmierz go, dowiedz się, dlaczego, a następnie zaimplementuj to, co jest potrzebne, aby było szybkie.

Przedwczesna optymalizacja

  • Dokonuj założeń dotyczących tego, co jest szybkie lub wolne bez pomiarów, zbuduj te założenia w sposobie projektowania i pisania kodu.

Te trzy rzeczy nie są takie same .

W szczególności Inżynieria wydajności jest czymś, co powinieneś robić od samego początku i zdecydowanie nie jest tym samym co Optymalizacja przedwczesna, ponieważ opiera się na rzeczywistych pomiarach; w tym przypadku opierając się na doświadczeniu i zaleceniach, jak działa sprzęt i jakie wzorce użytkowania będą szybkie lub wolne.

Niezaprojektowanie pod kątem wydajności może prowadzić do tak złych wyników, jak konieczność całkowitego przeprojektowania i przepisania, zanim zauważysz, że coś jest nie tak.

Na przykład: wszyscy dostawcy GPU radzą, aby odczytywanie GPU z każdej ramki odbywało się powoli. Zaprojektowanie kodu w taki sposób, aby nie powodowało powtórzeń, nie jest przedwczesną optymalizacją .

Maximus Minimus
źródło
28
Dziękuję bardzo za wspomnienie o tym. Deweloperzy zawsze ogłaszają zło przedwczesnej optymalizacji dzięki poradom, które zostały sformułowane wyjątkowo, aby o tym powiedzieć, ale często mniej doświadczeni programiści nie rozumieją niuansów. Zaprojektuj go poprawnie od samego początku. To NIE jest przedwczesna optymalizacja. Mówię, że POWINNAś być świadomy wydajności każdej linii, którą piszesz. Jako programista powinieneś znać najlepsze praktyki, aby nie robić głupich rzeczy. To powiedziawszy, nie powinieneś sparaliżować się optymalizacją. Najpierw weź coś do biegania, a następnie skup się na tym, co go spowalnia.
AC
2
@AC Chciałbym również wskazać, że dobry projekt może zająć trochę czasu, ale ostatecznie oznacza to, że skończyłeś także szybciej, co oznacza więcej czasu na optymalizację w razie potrzeby. Moje doświadczenie (z jednym, w połowie wykonanym solowym projektem hobby, aby go poprzeć) jest takie, że gdy twój projekt stanie się wystarczająco duży, duże zużycie zajmuje 1) „Jak mam to wplatać we wszystko, co już istnieje”, i 2) „Cholera, kod, który napisałem tydzień temu, który idealnie pasuje, nie działa wcale z tym, co chcę robić dalej”. Dobry projekt (czego jeszcze nie zrobiłem) pomaga obu z nich.
Arthur
5
Nieprawidłowe zaprojektowanie wydajności może być również uważane za pesymalizację projektu. Jeśli więc przedwczesna optymalizacja jest źródłem wszelkiego zła , co to powoduje przedwczesną pesymalizację ? ; P
ninjalj
1
@Arthur „pozostało [jeszcze] czasu na optymalizację”? To również dobry sposób, aby na to spojrzeć, optymalizując własne ograniczone zasoby programistyczne (i przepływ pracy), aby uzyskać optymalny wpływ z wysiłków związanych z optymalizacją! Wiem też, że nie stosujemy prawidłowego przepełnienia stosu, ale dziwi mnie to, że mówimy o optymalizacji, a zasada Pareto nie została jeszcze wymieniona w odpowiedzi ... Nie widzę nawet słowa „procent”!
AC
1
Projektując struktury danych pod kątem dobrej wydajności, musisz wiedzieć, że komputery potrafią wydajnie, a czego nie. np. nowoczesne procesory zapętlające się na tablicach mogą przykładać ogromną siłę, szczególnie jeśli skonfigurujesz takie elementy, aby kompilatory mogły automatycznie wektoryzować się za pomocą SSE / AVX. (np. użyj tablic współrzędnych x i tablic współrzędnych y, a nie tablic struktur xy pair lub potrójnych xyz. Zobacz stosoverflow.com/tags/sse/info dla niektórych linków, zwłaszcza SIMD na Insomniac Games (GDC 2015)
Peter Cordes
22

Jeśli chcesz przeprowadzić optymalizację we właściwym czasie, miej wolne maszyny i korzystaj z nich. W przypadku małego sklepu dobrym rozwiązaniem jest użycie powolnego laptopa w drodze do pracy i szybkiego pulpitu w biurze. Dodatkową korzyścią jest to, że jeśli prowadzisz jednoosobowy sklep, zmusza cię to do właściwego wykonania kopii zapasowej całego środowiska kompilacji.

Używając wolnej maszyny, będziesz wiedział, kiedy potrzebujesz poprawić wydajność.

czy musimy myśleć o wydajności w każdym wierszu kodu podczas tworzenia gier?

Absolutnie nie. Wydajność wiersza kodu jest zwykle nieistotna w jakimkolwiek rozwoju. Musisz myśleć w kategoriach algorytmów i złożoności algorytmicznej , a nie w kategoriach linii. W wielu przypadkach przyzwoite programowanie oznacza, że ​​możesz uzyskać około 2-krotne przyspieszenie poprzez optymalizację linii kodu, ale wybór właściwego lub złego algorytmu spowoduje czynniki od dziesiątek do milionów.

Mówienie o przedwczesnej optymalizacji nie polega na tym, że optymalizacja jest złą rzeczą. Chodzi o ludzi, którzy nie optymalizują właściwej rzeczy. Twój czas jest jedną ze zmiennych, które należy zoptymalizować, co osiąga się przez nie marnowanie czasu na „optymalizację” funkcji, która jest wywoływana raz na 30 milisekund, a jej wykonanie zajmuje 100 mikrosekund.

Innymi słowy: tworzenie czegoś „szybciej” nie ma sensu. Twoim celem jest „wystarczająco szybko”. Robienie czegoś „szybszego”, co jest już „wystarczająco szybkie”, to strata czasu, a robienie czegoś „szybszego”, co wciąż nie jest „wystarczająco szybkie”, oznacza po prostu, że jest zbyt wolne. „szybszy” nie ma znaczenia, istotny jest tylko „wystarczająco szybki”.

Piotr
źródło
1
algorytmy to tylko połowa rozwiązania, bardzo ważne jest również prawidłowe wykorzystanie struktur danych. Ponadto, planuj z wyprzedzeniem ewentualne wprowadzenie wielowątkowości i 64-bitowej kompilacji - chociaż nie zawsze są one potrzebne, czasem te funkcje mają znaczenie i zwykle nie są łatwe / tanie w instalacji wstecznej, jeśli nie są planowane. (Znam niektóre gry cierpiące na oba te problemy).
hoffmale
Dużo rozwoju dokonuje się przy kompilacjach Debug, które są znacznie wolniejsze niż kompilacje Release. Oznacza to, że do rozwoju wymagana jest znacznie szybsza maszyna niż wymagana do uruchomienia gry z akceptowalną prędkością.
Jack Aidley
Wzorce dostępu do pamięci podręcznej są również ważne i mogą mieć wpływ na 10 lub 100 różnic. np. rozważ mnożenie naiwnej macierzy (dla dużej macierzy). Pętla nad jedną kolumną matrycy rzędów głównych jest okropna. np. to Q&A ma współczynnik 13 perf różnicy dla tej samej złożoności algorytmu dla dodania transpozycji przed pętlami, tylko do naprawienia okropnego wzorca dostępu. (Kolejny czynnik 10 lub więcej można uzyskać ze zoptymalizowanej biblioteki BLAS, która jeszcze bardziej troszczy się o pamięć podręczną i SIMD ...)
Peter Cordes
9

Nie, nie musisz sprawdzać po każdej linii, ponieważ nie każda linia ma związek z wydajnością. Zależy to głównie od częstotliwości wykonywania linii. Sekcja kodu, której wykonanie zajmuje 1 ms, jest całkowicie nieistotna, gdy jest wykonywana raz podczas uruchamiania gry, warta obejrzenia przy wykonywaniu każdej klatki i musi być zdecydowanie zoptymalizowana, jeśli zostanie wykonana dla każdego obiektu gry w każdej klatce.

Zależy to również od rodzaju tworzonej gry. Tworząc bardzo intensywną graficznie strzelankę 3D, musisz być bardziej świadomy wydajności niż podczas programowania gry RPG w stylu retro.

Ostatecznie najlepszą radą jest test, test, test. Zbuduj realistyczne sceny testowe dla swojej gry, które wyświetlają na ekranie nieco więcej rzeczy, niż można by się spodziewać w prawdziwej grze. Przetestuj na sprzęcie, który znajduje się w dolnej części docelowych specyfikacji. Upewnij się, że wydajność jest zgodna z oczekiwaniami. Gdy sytuacja się pogorszy, użyj profilera, aby znaleźć wąskie gardła.

Philipp
źródło
3

Musisz włożyć wystarczająco dużo wysiłku, aby przynajmniej przejść „czy to będzie potencjalnie wąskie gardło, a jeśli tak, to jak zaangażowana będzie zmiana, aby to naprawić?”.

Ogólnie rzecz biorąc, wiąże się to z równowagą między czasem / zasobami spędzonymi na ustalaniu, czy coś jest właściwym kierunkiem, a czasem / zasobami spędzonymi na podejmowaniu złych decyzji.

Pomyśl o tym trochę jak o remoncie mieszkania. Niektóre decyzje mogą mieć trwałe konsekwencje („Ups, lodówka nie mieści się już w jej kąciku.” „Ups, w którym chcę postawić swoją łazienkę nie ma wody”), ale nie pomyślisz o tym, jak każde pociągnięcie pędzlem może wpływać np. na stabilność konstrukcyjną budynku.

Lub, inaczej mówiąc, nie cofaj się w kąt. Lub przynajmniej wiedz, kiedy cofasz się w kąt i upewnij się, że jest to róg, w którym chcesz wrócić. Na przykład:

  • Czy wiążę szybkość aktualizacji gry z graficzną szybkością klatek?
  • Czy wymuszam synchroniczne zapisywanie?
  • Czy wymuszam (nie) determinizm?
TLW
źródło
1

W grze Twoim głównym celem jest osiągnięcie docelowej prędkości klatek na docelowej maszynie o minimalnej specyfikacji (i ewentualnie maksymalnym czasie ładowania itp.).

Aby to zrobić, nie musisz się martwić po każdej linii.

Musisz martwić się wcześniej, ilekroć wybierzesz określoną strategię, algorytm lub kontener. Jeśli dosłownie uniemożliwisz osiągnięcie celu poprzez podejmowanie niewłaściwych decyzji projektowych, wówczas jakakolwiek optymalizacja, którą możesz zrobić później, nie przyniesie rezultatu.

Następnie musisz się martwić, gdy coś jest równoległe lub równoległe. Gry są masowo równoległe, jeśli nie bez innego powodu, to dlatego, że grafika jest.
Dlatego równoległe oznacza nie tylko „wątki”, ale także na przykład graficzny interfejs API, dostęp do dysku lub sieć. Ilekroć tracisz szansę posiadania czegoś, co można łatwo i natywnie równolegle uruchomić równolegle, na przykład z powodu złej synchronizacji (lub w ogóle z powodu niestosowania asynchronicznego interfejsu API), tracisz więcej niż kiedykolwiek możesz zoptymalizować za pomocą innych środków.
Musisz się również martwić, gdy coś się pojawijest dobrze znany jako wąskie gardło lub źródło kramów lub przeszkodę w osadzaniu się kamienia. Takich jak na przykład przełączanie stanów renderowania, rysowanie połączeń, odczytywanie z GPU lub otwieranie plików.

Na koniec, gdy skończysz, a testy wykażą, że nie osiągasz docelowej liczby klatek, musisz zoptymalizować. Znajdź jedno największe wąskie gardło, które zajmuje 90% czasu, i zoptymalizuj to. Jeśli to nie wystarczy, znajdź sekundy największe.
Jeśli osiągniesz cel, gratulacje. Idź dalej i zapomnij o tym.

Damon
źródło
0

Zejdź na ziemię. Odpowiedź jest po prostu całkowicie NIE. Bez dyskusji.

Głównym problemem jest sposób, w jaki pytasz:

czy musimy myśleć o wydajności w każdym wierszu kodu podczas tworzenia gier?

Masz na myśli również takie rzeczy jak ekrany konfiguracji? Jak jednorazowy parser dla małego pliku konfiguracyjnego o maksymalnej wielkości 1kb? Poważnie?

Nie ma znaczenia, jak ważna jest większość czasu w twoim kodzie. Nie ma jednego oprogramowania, w którym każda linia ma krytyczne znaczenie dla czasu. Nigdzie.

TomTom
źródło