Zwiększenie wydajności: Globalna inwalidacja VALORANT
Cześć!
Z tej strony Aaron Cheney, programista z zespołu ds. wydajności
VALORANT. Wydajność to kluczowy element zapewniający integralność
rywalizacji w VALORANT, a nasz zespół odpowiada za monitorowanie,
utrzymywanie i ulepszanie zarówno wydajności serwerów, jak i
klienta.
Bardzo
się cieszymy, że możemy udostępnić informacje o funkcji, nad
którą pracowaliśmy kilka miesięcy: globalnej
inwalidacji.
Niedługo szczegółowo opiszemy tę funkcję, najpierw przyjrzyjmy
się jednak temu, jak wpłynęła ona na wydajność klienta od
pojawienia się patcha 4.03.
Uwaga, spoiler: wpłynęła całkiem nieźle.
Globalna
inwalidacja zapewniła spore usprawnienia dla dużej części naszych
graczy. Właściwie jest
to największy wzrost wydajności klientów od czasu premiery gry.
Chociaż
te diagramy są ekscytujące – i jesteśmy zachwyceni wynikami –
ważne, abyście wiedzieli, na co dokładnie patrzycie. Pracujemy ze
sporymi, złożonymi zbiorami danych. Porządkując, filtrując i
kontrolując te informacje, jesteśmy w stanie lepiej zrozumieć, w
jaki sposób odbieracie rozgrywkę. Oto, o czym musicie pamiętać,
żeby mieć pełen obraz sytuacji:
- Wykresy zestawiające zmienne „patch” i „średnia wartość FPS”: im wyższa wartość, tym lepiej.
- Każda linia przedstawia konfigurację sprzętową (kombinację CPU+GPU) często spotykaną wśród naszych graczy. Analizując dane dotyczące wydajności, uważamy tę kombinację za najważniejszy czynnik prognostyczny spodziewanej wydajności. Na tych diagramach sprzęty z tą samą kombinacją CPU+GPU zostały zestawione razem.
- Próbki danych pobrano z dwóch kolejek: nierankingowej i rankingowej. Ponieważ są to najpopularniejsze tryby gry w VALORANT, nie szczędzimy wysiłków, aby zrozumieć i poprawić ich wydajność.
- Wykluczyliśmy gry, w których nie uczestniczy dokładnie dziesięciu graczy. Dzięki temu skrajne wartości nie zniekształcają danych (w grach z mniejszą liczbą osób wydajność jest lepsza).
PODSUMOWANIE GLOBALNEJ INWALIDACJI
Globalna inwalidacja zapewnia do 15% wzrostu klientom ograniczonym przez procesor (ogólnie rzecz biorąc, chodzi o sprzęt średniej i wyższej klasy). Aby takie wzrosty mogły zaistnieć, potrzebny był wysiłek licznych zespołów oraz wiele miesięcy pracy. Nasz proces identyfikacji zdatnych do optymalizacji obszarów gry opłacił się, a równoległe zarządzanie ryzykiem pomogło zapewnić graczom stabilną rozgrywkę.
Kto skorzystał; dostosowanie oczekiwań
Z
naszych aktualnych parametrów wynika, że globalna
inwalidacja zwiększyła o do 15%
wydajność tych konfiguracji klienta, które są ograniczone przez
procesor
(ogólnie rzecz biorąc, chodzi o sprzęt ze średniej i wyższej
półki).
Chociaż
w sumie tendencja wzrostowa jest zauważalna, nie odzwierciedla ona
jakości rozgrywki będącej w toku. Ponadto nie ma gwarancji, że
rezultaty będą identyczne w przypadku każdego komputera z takim
samym sprzętem.
Oznacza to, że podstawowa wydajność VALORANT na komputerach ograniczonych przez procesory zasadniczo wzrosła, jednak wydajność waszego sprzętu zależy od różnych, unikalnych w przypadku każdego z was czynników.
GLOBALNA INWALIDACJA OD PODSZEWKI
Zanim dokonamy właściwego przeglądu globalnej inwalidacji, wyjaśnijmy sobie kwestię elementów interfejsu w silniku Unreal Engine.
Widżety i drzewa
Elementy
interfejsu (zwane również widżetami) tworzone są z mniejszych
części składowych przy użyciu struktury drzewa. Jest ona
analogiczna do systemu plików w komputerze. Widżet może mieć
dowolną liczbę widżetów podrzędnych (tak jak folder może mieć
dowolną liczbę plików).
Te
części składowe można łączyć, aby stworzyć złożone widżety.
Na przykład nasz licznik amunicji składa się z wielu części, a
jego drzewo wygląda mniej więcej tak:
Po zestawieniu wszystkiego razem, elementy składowe licznika amunicji wyglądają tak:
(Czerwone obrysy powyżej zostały nadmiernie zarysowane dla przejrzystości. W praktyce wiele z nich nachodziłoby na siebie).
Zawsze, gdy co najmniej jeden widżet ulega zmianie w obrębie struktury drzewa, zaistniała modyfikacja może wpłynąć na wiele innych widżetów. Jeśli na przyk��ad jakiś widżet zostanie przeniesiony w nowe miejsce na ekranie, wszystkie znajdujące się pod nim widżety też muszą ponownie obliczyć własną pozycję. Wspomnianymi zmianami zarządza system zwany „inwalidacją”, który omówimy w kolejnej części tego artykułu.
Inwalidacja
Inwalidacja
to mechanizm używany przez silnik Unreal Engine do sygnalizowania,
gdy określony widżet został zmieniony i wymaga aktualizacji.
Widżet
wymaga inwalidacji z wielu powodów dotyczących animacji, koloru,
nieprzezroczystości, rozmiaru, szeregowania, tekstu, obrazów i
wielu innych właściwości, które mogą ulec zmianie w reakcji na
coś, co dzieje się w grze. Gdy wspomniane zmiany dotkną widżetu,
podlega on „inwalidacji”, co oznacza, że jest konieczna jego
aktualizacja.
Ten
proces ma jednak jeszcze bardziej skomplikowany przebieg, ponieważ
widżet może podlegać wielu rodzajom inwalidacji. Należą do nich:
- Układ – gdy zmianie uległ rozmiar widżetu (bardzo kosztowna zmiana).
- Malowanie – gdy zmienił się wygląd widżetu, ale rozmiar pozostał ten sam.
- Porządek widżetów podrzędnych – gdy zmienił się porządek widżetów wewnątrz drzewa (wiąże się to również z układem, a zatem jest to kosztowna zmiana).
- Widoczność – gdy widoczność widżetu uległa zmianie: albo stał się niewidoczny, albo widoczny (również wiąże się z układem, a zatem jest to kosztowna zmiana).
Takie
rodzaje inwalidacji stosowane są po to, by określić, jakie
operacje należy wykonać, aby prawidłowo narysować widżet.
Sprawa
staje się jeszcze bardziej złożona, gdy jeden widżet zależy od
innego. Widżety uporządkowane są według hierarchii, a ich układ
uzależniony jest od szeregu czynników. Inwalidacja jednego widżetu
może wymagać inwalidacji szeregu innych z nim powiązanych w celu
wykonania prawidłowego rysunku. Jeśli na przykład kilka widżetów
jest ułożonych w pionie (np. panel społeczności z listą
znajomych), a ogólny porządek widżetów ulegnie zmianie (np.
znajomy pojawi się online), wszystkie ułożone w pionie widżety
muszą zostać zaktualizowane.
Takiemu
systemowi przyświeca kilka celów:
- Inwalidacja możliwie jak najmniejszej liczby widżetów. W ten sposób ograniczona zostaje liczba widżetów wymagających aktualizacji w celu wykonania prawidłowego rysunku.
- Inwalidacja widżetu tylko w razie konieczności. Nieprawidłowa inwalidacja widżetu prowadzi do zmarnowania cennych cykli CPU.
- W razie braku inwalidacji widżetu – zbuforowanie wyniku umożliwiające szybkie narysowanie go w każdej klatce. Jeśli nic się nie zmieni, wówczas można oszczędzić cykle procesora.
Dzięki
temu wprowadzeniu łatwiej będzie zrozumieć, na czym polega
aktualizacja widżetów i co może powodować inwalidację. A teraz
zobaczymy, jak deweloperzy stosują ten mechanizm w praktyce.
Komponenty Invalidation Box
Silnik
Unreal Engine zawiera komponent – zwany Invalidation Box („pudełko
inwalidacji”) – umożliwiający grupowanie wielu widżetów.
Wszystkie widżety znajdujące się w pojedynczym Invalidation Box
nie zostaną poddane wstępnemu przejściu, zaznaczeniu ani
malowaniu. Zamiast tego wynik jest buforowany w buforze wierzchołków.
Za
każdym razem, gdy objęty Invalidation Box widżet zostaje poddany
inwalidacji, buforowane dane zostają odrzucone, a widżet
zaktualizowany i ponownie pomalowany. Chociaż odświeżanie pamięci
podręcznej może okazać się kosztowne w przypadku pojedynczej
klatki, to w dłuższej perspektywie zamortyzowany wynik będzie
znacznie lepszy.
Komponenty
Invalidation Box są kluczowym elementem warunkującym wydajność
interfejsu VALORANT, zwłaszcza w okresie poprzedzającym premierę.
Nie są one jednak zupełnie bezkosztowe:
- Deweloperzy muszą wiedzieć, które widżety nadają się do grupowania w obrębie Invalidation Box. Nie nadaje się do tego żaden widżet, który jest regularnie aktualizowany.
- Umieszczanie widżetów w Invalidation Box wymaga od dewelopera ręcznego nakładu pracy. Nie jest to wykonalne w przypadku każdego widżetu w grze, dlatego deweloperzy muszą też wiedzieć, które z widżetów warto umieścić w Invalidation Box.
Więcej
informacji na temat Invalidation Boxes znajdziecie w dokumentacji
Epic (link do artykułu po angielsku).
Zarysowaliśmy już wystarczająco obszerny kontekst, aby omówić globalną inwalidację!
Nadejście globalnej inwalidacji
W
tym momencie być może zadajecie sobie pytanie: „Dlaczego nie
umieścić każdego elementu interfejsu użytkownika w globalnym
komponencie Invalidation Box?”. Tak się składa, że właśnie o
to chodzi (mniej więcej) w globalnej inwalidacji.
Globalna
inwalidacja ma za zadanie znacząco zwiększyć wydajność
interfejsu użytkownika w całej grze, jednocześnie ograniczając
ręczne działania wymagane od deweloperów w celu umieszczenia
widżetów w komponentach Invalidation Box.
Tak wyglądałby przebieg procesu według idealnego scenariusza.
Jednak
w przypadku UE4.25 (wersji silnika Unreal Engine używanej w
VALORANT) globalna inwalidacja nie oferuje powszechnej obsługi
wszystkim rodzajom widżetów. W późniejszych wersjach Unreal
Engine wprowadzono ulepszenia, z których VALORANT nie mógł jednak
od razu skorzystać. Ponadto nie wiedzieliśmy do końca, o
ile szybciej
będzie działać VALORANT dzięki globalnej inwalidacji.
I
właśnie tutaj zaczęła się nasza praca.
DLACZEGO SIĘ NA NIĄ ZDECYDOWALIŚMY?
Pod
koniec lipca 2021 r. nasz zespół postanowił sprawdzić globalną
inwalidację w ramach testów wewnętrznych. Wprowadzono niewielkie
zmiany w celu naprawienia kilku błędów, aby z powodzeniem
przeprowadzić testy. Wiedzieliśmy jednak, że błędy pojawią się
właśnie w trakcie tych testów... i oczywiście je znaleźliśmy.
Pod
koniec testowania uzbieraliśmy ich około 20, przy czym mówimy o
najbardziej ewidentnych problemach. Drobniejsze, podstępne błędy
prawdopodobnie czekały, aż je wykryjemy. Nie wspominając o tych
spoza spektrum naszych podstawowych zainteresowań, których nie
przetestowaliśmy.
Tylko
czy... globalna inwalidacja doprowadziła do wzrostów wydajności?
Jak najbardziej.
Po
analizie danych z pojedynczego testu ustalono, że interfejs
użytkownika był szybszy o ~35%.
(Uwaga: interfejs odpowiada tylko za część kosztu pojedynczej
klatki).
Wciąż
pozostawały jednak otwarte pytania:
- Ile czasu zajmie naprawienie wszystkich błędów?
- Które zespoły powinny odpowiadać za to zadanie?
- Czy ma ono pierwszeństwo przed innymi zaplanowanymi pracami? Aby zdążyć na czas z regularnymi premierami zawartości, nasze harmonogramy często ustalamy z kilkumiesięcznym wyprzedzeniem i trudno jest nam w nich znaleźć miejsce na inne pojawiające się prace – nawet tak ekscytujące jak ta.
- Czy naprawa błędów negatywnie wpłynie na wzrosty wydajności? Naprawienie wszystkich błędów wymagałoby wielu zmian w kodzie, a każda z nich mogłaby sprawić, że interfejs użytkownika stałby się kosztowniejszy.
- Kiedy powinniśmy przeprowadzić takie prace? Wiedząc, że globalna inwalidacja jest przedmiotem dalszych prac w późniejszych wersjach silnika Unreal Engine, musieliśmy zastanowić się nad harmonogramem wprowadzania zmian w związku z aktywnością Epic.
Ostatecznie
uznaliśmy, że z kilku powodów wysiłek ten jest wart zachodu.
Integracja z silnikiem Unreal Engine i harmonogram
Mimo
że w Unreal Engine w wersjach 4.26 i 4.27 poczyniono znaczące
postępy w zakresie globalnej inwalidacji, VALORANT prowadzi prace
integracyjne zgodnie z opóźnionym harmonogramem. Nie pracujemy na
„najświeższej” wersji, aby ograniczyć czynniki ryzyka i
zapewnić graczom stabilność rozgrywki.
Nasz
harmonogram przewidywał, że przez wiele miesięcy pozostaniemy przy
silniku Unreal Engine 4.25. To oznaczało, że gracze nie
skorzystaliby z tych wzrostów wydajności jeszcze przez ponad rok.
Nie spodobało nam się to.
Więcej
informacji na temat podejścia VALORANT do ulepszeń silnika Unreal
Engine znajdziecie w tym
wątku na Twitterze, którego autorem jest dyrektor ds. technologii w
VALORANT, Marcus Reid.
Znane wzrosty wydajności
Globalna
inwalidacja oferowała coś naprawdę wyjątkowego w kontekście
wysiłków skupionych na wydajności: wartość
mierzoną.
Zmierzyliśmy potencjalne wzrosty w trakcie testów wewnętrznych.
Przebieg drogi do osiągnięcia tych wartości nie budził
(większych) wątpliwości.
Prace
nad wydajnością są trudne. Decydują o niej bardzo nieznaczne
wartości. Stopniowe zmiany pomagają zwiększyć ogólną wydajność
wraz z upływem czasu i rzadko się zdarza, żeby pojedyncza
modyfikacja spowodowała dwucyfrowe wzrosty. Nie mogliśmy nie
skorzystać z tak dobrej optymalizacji.
Nawet
po uwzględnieniu zmniejszonych wzrostów wynikających z poprawek
błędów globalna inwalidacja dawała nam największe szanse na
zapewnienie graczom znaczących wzrostów w rozsądnym czasie.
JAK UDAŁO SIĘ NAM PRZEPROWADZIĆ TE PRACE?
Chociaż wstępne eksperymenty z globalną inwalidacją rozpoczęły się pod koniec lipca 2021 r., konkretne wysiłki zmierzające do ustabilizowania tej funkcji zostały podjęte dopiero pod koniec września 2021 r.
Selektywna integracja zmian wprowadzonych przez Epic
Pełna
integracja Unreal Engine w wersjach 4.26 i 4.27 nie wchodziła w
rachubę. Ponieważ jednak wiedzieliśmy, że Epic aktywnie pracował
nad globalną inwalidacją, postanowiliśmy przekopać się przez
tysiące zmian i wybrać te, które mogły przybliżyć nas do
stabilnej, w pełni funkcjonalnej globalnej inwalidacji.
Częściowa
integracja zmian stanowiła nie lada wyzwanie. Musieliśmy
zmodyfikować jak najmniej podstawowych funkcji silnika, aby utrzymać
stabilność, wdrażając jednocześnie odpowiednie zmiany z Epic.
Wszystkie te prace wykonywano nie w głównej, tylko w odrębnej
gałęzi projektowej VALORANT, aby nie wpłynęły one na pozostałych
deweloperów.
Po
selektywnym włączeniu zmian Epic do naszego silnika przez kilka
kolejnych tygodni naprawialiśmy tyle błędów, ile się dało,
przygotowując się w tym samym czasie do wprowadzenia globalnej
inwalidacji do głównej gałęzi VALORANT. W międzyczasie
zaprojektowaliśmy
przełącznik umożliwiający nam szybkie włączanie i wyłączanie
tej funkcji
(na wypadek jakiejś katastrofy).
Po dokonaniu wielu poprawek błędów oraz integracji licznych zmian wprowadzonych przez Epic w wersjach 4.26 i 4.27 z powrotem połączyliśmy odizolowaną gałąź z główną.
Odkrywanie podobieństw między błędami
Chociaż wiele błędów związanych z globalną inwalidacją przejawiało się na różne sposoby, ich podstawowa przyczyna często tkwiła w jednym problemie. Naprawienie tego rodzaju błędów było najbardziej opłacalne, ponieważ dzięki temu za pomocą jednej zmiany można było usunąć wiele problemów. Przykładowo jedna modyfikacja usunęła ponad 10 błędów rozsianych po całej grze. Dogłębna analiza podstawowego problemu pozwoliła opracować solidne rozwiązania, które zwiększyły niezawodność i stabilność globalnej inwalidacji.
Wykrycie błędów, naprawa błędów, testy – i tak w kółko
W kolejnych tygodniach i miesiącach trwał regularny cykl uruchamiania globalnej inwalidacji przed testami, wykrywania szeregu błędów, wyłączania globalnej inwalidacji po testach oraz rozwiązywania zidentyfikowanych problemów.
Z
każdą kolejną pętlą raportowano coraz mniej błędów.
Kontynuowaliśmy ten proces, dopóki nieprzerwany strumień błędów
nie zamienił się w ich powolne skapywanie, aż w końcu ustał.
Do końca listopada 2021 r. wszystkie główne problemy zostały rozwiązane, a globalna inwalidacja była w dużej mierze stabilna.
Istotne błędy
- Astra powoduje awarię gry – w pewnym momencie każdy, kto nią grał, napotykał na awarię zaraz po wczytaniu gry. Ten błąd został naprawiony po integracji zmian wprowadzonych przez Epic.
- Awaria wielokrotnego dziedziczenia – wielokrotne dziedziczenie w C++ to trudny temat. Nie zagłębiając się zbytnio w szczegóły, kolejność destruktorów w konkretnej klasie nie była wykonywana w odpowiednim porządku, co skutkowało awarią. Prosta zamiana dwóch linijek kodu w celu modyfikacji kolejności dziedziczenia pozwoliła pozbyć się problemu. Aby dowiedzieć się więcej o wielokrotnym dziedziczeniu, zajrzyjcie na tę stronę (link do artykułu po angielsku).
- Nieskończony dźwięk czatu – podczas korzystania z menu pasek czatu odtwarza dźwięk po najechaniu na niego myszą. Ku irytacji wszystkich błąd powodował, że dźwięk był odtwarzany wiele razy na sekundę. Aby usunąć ten problem, musieliśmy zrozumieć okoliczności, w jakich widżety odbierają zdarzenia myszy wiele razy w obrębie klatki.
Metodologie testów
Jednym
z elementów globalnej inwalidacji, który skłonił nas do
zachowania szczególnej ostrożności (a tym samym do gruntownych
testów), jest fakt, że wpływa
ona na każdy aspekt gry.
Dosłownie.
Na
listę waszych znajomych? Jak najbardziej. Na przycisk, który
naciskacie, aby dołączyć do kolejki? Też. Na menu ustawień? No
pewnie. Na procent moich strzałów w głowę? No cóż...
Chodzi
o to, że elementy interfejsu użytkownika występują w całej grze
i często są źródłem istotnych informacji dla graczy.
Niesprawność
chociażby jednego z tych elementów interfejsu była
niedopuszczalna.
Aby temu zapobiec, nasz dział kontroli jakości stworzył plan testowania obejmujący wiele strategii, aby mieć pewność, że globalna inwalidacja działa zgodnie z założeniami.
Testowanie pionowych wycinków
„Pionowy wycinek” w VALORANT oznacza główną ścieżkę zwykle obieraną przez graczy: od uruchomienia klienta, przez dołączanie do kolejki, aż po rozgrywkę całego meczu i interakcję z ekranem końca gry. Skupiając się na istotnych elementach gry, dział kontroli jakości był w stanie szybko przetestować najczęściej używane elementy i wcześnie wykryć problemy.
Testowanie destruktywne
Tam, gdzie kończy się testowanie pionowego wycinka, zaczyna się testowanie destruktywne. Jego celem jest wykrycie odbiegających od normy problemów, często poprzez modyfikację czynników zewnętrznych (takich jak ping sieci, liczba klatek na sekundę, użycie skrótu Alt+Tab itp.). Wyposażony w szereg wewnętrznych narzędzi dział kontroli jakości poświęcił kilka tygodni na ten model testowania.
Testowanie przypadków brzegowych
Istnieje wiele aspektów VALORANT, z którymi ma do czynienia tylko niewielki procent graczy. Z niektórymi – np. przeznaczonymi dla nowych graczy – mają oni styczność tak naprawdę tylko raz. To, że korzysta się z nich rzadziej, nie znaczy, że są mniej ważne. Wykrycie i testowanie wszystkich przypadków brzegowych pomogło nam wyłapać ukryte błędy.
Testowanie na PBE (publicznym serwerze testowym)
PBE
był kluczowym etapem wdrażania globalnej inwalidacji.
- Po raz pierwszy gracze z zewnątrz mogli przetestować tę funkcję. Oznaczało to, że globalna inwalidacja może zostać przetestowana w warunkach „rzeczywistych”.
- Na publicznym serwerze testowym pojawiają się rozmaite specyfikacje sprzętowe. PBE celowo obejmuje zakres od słabszych do mocniejszych specyfikacji. Dzięki testom szerokiej gamy urządzeń należących do graczy zyskaliśmy pewność, że globalna inwalidacja będzie działała dobrze wśród szerszego grona osób.
Po
testach PBE w weekend od 22 do 23 stycznia 2022 r. byli��my już
przekonani, że globalna inwalidacja nie zakłóca integralności
gry, a wzrosty wydajności są zgodne z naszymi przewidywaniami.
Premiera globalnej inwalidacji
Po
premierze globalnej inwalidacji w patchu 4.03 uważnie
monitorowaliśmy zgłoszenia graczy dotyczące błędów. Bacznie
przyglądaliśmy się również danym dotyczącym wydajności, aby
potwierdzić, że nasze szacunki pokrywają się z uzyskanymi
wynikami. Ostatecznie globalna inwalidacja okazała się ogromnym
sukcesem, z którego mogli skorzystać gracze. Mamy też nadzieję,
że podoba wam większa wydajność mierzona w klatkach na sekundę.
Globalna
inwalidacja została już udostępniona, więc zespół do spraw
wydajności wraca do pracy, żeby zapewnić wam jeszcze więcej
wzrostów. Do następnego. Przyjemnego polowania!