Wysoki CPU main :(


#1

Witam :slight_smile:

Posiadam najnowszą wersję Unity 3D i ostatnio napotykam dość irytujący problem:
image

Mam:
image

Latałem już po googlach youtubach i dalej nic :frowning:

Jak się dzieje na planszy to nawet do 20 ms :frowning:

Co mogę zrobić żeby zredukować chociaż o parę ms?


#2

Nie jestem ekspertem od optymalizacji, ale skoro nikt się nie odzywa…
To są rzeczy, który zrobiłbym w pierwszej kolejności:

  1. Za pomocą informacji z FrameDebuggera postarał się zmniejszyć ilość batchów.
  2. Wyłączył animację i zobaczył jaki będzie przyrost klatek/zmniejszenie obciążenia cpu.
  3. Pokombinował z ustawieniami cieni i świateł (np wyłączył realtime dla świateł wszędzie tam, gdzie nie jest on konieczny, zmniejszył zasięg per-pixel lights, etc…).
  4. Upewnił się, że static i dynamic batching jest włączone.
  5. Wyłączył wszystkie niepotrzebne shadow castery/receivery.
  6. Nieporuszające się obiekty ustawił jako static.
  7. To samo z light probes/reflection probes.
  8. Zastanowił się, czy da się zmniejszyć wykorzystanie GC i czy w ógóle można gdzieś użyć Object-Pool pattern.
    Większośc rzeczy możesz też zrobić automatycznie/globalnie w ustawieniach jakości grafiki.
    Generalnie, chodzi o to, żeby jak najbardziej zmniejszyć ilość komunikacji CPU-GPU (np. przełączanie między materiałami, wysyłanie nowych meshy) oraz zmniejszyć ilość elementów, które muszą być przetwarzane przez CPU.
    Przedstawione wyżej metody optymalizacji są ogólne. Dla twojego konkretnego problemu rozwiązanie może wyglądać zupełnie inaczej, ale podałeś zbyt mało informacji, żebym był w stanie powiedzieć coś więcej. Może ktoś inny będzie mógł pomóc lepiej.

#3

Spróbuj wyciągnąć więcej informacji używając Deep Profile. Możesz też zbudować wersję dev i sprawdzić w profilerze czy jest to samo. Czasem się zdarza że coś w edytorze zapycha GC a w playerze już nie, i tym pierwszym wtedy nie trzeba się przejmować.

Szybki porada u wujka Google zwróciła mi takie coś, może się przyda
https://forum.unity.com/threads/stuttering-performance-issues.496456/


#4

A dziękuję za podpowiedzi :slight_smile:

Sprawdziłem raz jeszcze, i okazało się, że Update dla mobów jest “zawalona” rzeczami… . No i teraz nasuwa się pytanie :slight_smile: Gdzie mam w takim razie dać ich logikę, skoro to będzie to samo? Nie używałem Latepdate i UpdateFixed (jakoś tak). Jeśli to mój błąd to zaraz zastosuję :slight_smile: jeśli to coś da. W przeciwnym wypadku będę zmuszony się pogodzić, bo nawet po redukcji mobów to nic nie zmienia oO Nawalało mnie na razie 50 mobów a main cpu odczytał nawet 30 ms :frowning:

Aby przybliżyć problem posłużę się własnym postem, który ma prawie te same skrypty:


#5

Ja w Skullstone całą logikę przerzuciłem na osobny wątek. Główny wątek służy mi jedynie do aktualizacji stanów geometrii oraz wywołań renderowania. Nie mam więc praktycznie żadnych opóźnień.
Aaaa, tylko że nie robiłem tego w Unity :slight_smile: Unity umie obsługiwać wątki ale tego typu ‘konstrukcja’ gry wykracza chyba znacznie poza ramy tego silnika. Nie jestem pewien, bo Unity dopiero się uczę, ale tak z czysto programistycznego punktu widzenia to radził bym zainteresować się tematem wielowątkowości.

EDIT:
A tak na zdrowy rozum: czy kazdy mob co klatkę musi wykonywać całość swojego ‘update’ ? Taki update zawiera pewnie bardzo ciężkie operacje typu pathfinding, AI itp. Mam rację? A gdyby tak podzielic update na dwie części, lekką i ciężką. Tą lekką wykonywać co klatkę, ciężką np co pewien czas (500ms + random). Dla gracza niezauważalne, a ilośc operacji w każdej klatce drastycznie spadnie :wink:


#6

Zmniejszenie ilości wywołań to oczywiście dobry pomysł, ale ja spróbowałbym najpierw zoptymalizować już istniejący kod. Upewnij się, czy problemem jest właśnie ten kawałek kodu (i jaki dokładnie), używając podczas profilowania:
Profiler.BeginSample(“MyAwesomeSampleName”);

Profiler.EndSample();

i z wyłączonym profilowaniem:
System.Diagnostics.Stopwatch sw0 = System.Diagnostics.Stopwatch.StartNew();

sw0.Stop();
long resultTicks = sw0.ElapsedTicks;
Debug.Log(“Result:” + resultTicks + " ticks");

Standardowymi miejscami do optymalizacji są pętlę. Zwłaszcza te typu x*y.
Warto też pomyśleć o odciążeniu GC i zmniejszeniu ilości alokacji (ma to szczególne znaczenie, jeśli grę chcesz wypuścić na web, jako że GC działa w tym środowisku max raz na klatkę)
Dodatkowo, jak już ktoś ci powiedział spróbuj sprofilować build działający na docelowej platformie. Często można się zdziwić, często na niekorzyść.
Jak już wyizolujesz kod, to wrzuć go tutaj, może coś doradzimy.


#7

Nie nie :slight_smile: Ten prototyp jest tylko dla mnie dla potrzeby CV. Z miłą chęcią bym udostępnił skompilowany test, lecz zawsze ktoś ciekawski może mi go dekompilować.

Wiecie co? Po uruchomieniu skompilowanej gry, dziala ona bardzo plynnie :slight_smile:


#8

Bo jakby ktos przegladnal screena to by zobaczyl ze spowalnia cie EDITORoverhead… i wystarczylo wpisac w google…
ogolnie masz duzo GC Alloc (na wykresach)


#9

Myślę, że każdy to zauważył, ale…
Informacje podawane w overlayu statystyk renderowania okna Game biorą pod uwagę wyłącznie to co jest renderowane w oknie Game i nie liczą narzutu związanego z samym edytorem.
Oznacza to tyle, że zaznaczony przez klawira problem zbyt dużego obciążenia procesora (według niego, bo nie wiemy czy to obciążenie nie jest adekwatne do posiadanego sprzętu etc.) wciąż może istnieć.
W buildzie nie widać go po prostu ze względu na brak editor overhead.
Optymalizacja editor overhead to już zupełnie inna para kaloszy, której bez dokładnych informacji nie ma nawet co zaczynać. Jedyna co mogę doradzić na szybko to przed naciśnięciem Play odznaczyć game obiekt w oknie hierarchii, żeby zamknąć inspektora. (Edit:) Ewentualnie, jeśli inspektor jest bardzo potrzeby przestawić go w tryb debugowania.


#10

Próbowałem coś z tym zrobić, ale coś mi nie wychodzi :frowning:
Na chwilę obecną to wygląda w ten sposób:



#11

Z tego screenshota wynika tylko, że overhead jest w normie, natomiast problem, jeśli istnieje, to leży w logice wykonywanej w Update()
Dodaj do kodu:
Profiler.BeginSample (“MyPieceOfCode”);
//Twój podejrzany kod
Profiler.EndSample ();
… w miejscach, gdzie podejrzewasz że znajduje się ciężki kod, następnie sprofiluj, rozwiń całą hierarchię Update.ScriptRunBehaviourUpdate i zrób screena.
edit: oczywiście “MyPieceOfCode” modyfikujesz na np “SampleA”, “SampleB”, etc…