Programiści powinni mieć dość dobry pomysł na koszt niektórych operacji: na przykład koszt instrukcji procesora, koszt braku pamięci podręcznej L1, L2 lub L3, koszt LHS.
Jeśli chodzi o grafikę, zdaję sobie sprawę, że nie mam pojęcia, czym one są. Mam na uwadze, że jeśli zamawiamy je według kosztów, zmiany stanu są następujące:
- Zmiana jednolitego shadera.
- Aktywna zmiana bufora wierzchołków.
- Aktywna zmiana jednostki tekstury.
- Zmiana programu aktywnego modułu cieniującego.
- Aktywna zmiana bufora ramki.
Ale jest to bardzo ogólna zasada, może nawet nie być poprawna i nie mam pojęcia, jakie są rzędy wielkości. Jeśli próbujemy umieścić jednostki, ns, cykle zegara lub liczbę instrukcji, o czym mówimy?
źródło
Rzeczywisty koszt jakiejkolwiek zmiany stanu różni się w zależności od tak wielu czynników, że ogólna odpowiedź jest prawie niemożliwa.
Po pierwsze, każda zmiana stanu może potencjalnie mieć zarówno koszt po stronie procesora, jak i po stronie procesora graficznego. Koszt procesora może, w zależności od sterownika i interfejsu graficznego, być opłacany w całości w głównym wątku lub częściowo w tle.
Po drugie, koszt GPU może zależeć od ilości pracy w locie. Współczesne układy GPU są bardzo rozbudowane i uwielbiają wykonywać wiele zadań jednocześnie, a największym spowolnieniem jest zatrzymanie rurociągu, tak aby wszystko, co jest aktualnie w locie, musiało przejść na emeryturę przed zmianą stanu. Co może spowodować przeciągnięcie rurociągu? To zależy od twojego GPU!
Rzeczą, którą musisz wiedzieć, aby zrozumieć wydajność, jest: co sterownik i karta graficzna muszą zrobić, aby przetworzyć zmianę stanu? Zależy to oczywiście od twojego procesora graficznego, a także od szczegółów, które ISV często nie udostępniają publicznie. Istnieją jednak pewne ogólne zasady .
Procesory graficzne są ogólnie podzielone na frontend i backend. Frontend obsługuje strumień poleceń generowanych przez sterownik, podczas gdy backend wykonuje całą prawdziwą pracę. Jak powiedziałem wcześniej, backend uwielbia mieć dużo pracy w locie, ale potrzebuje pewnych informacji do przechowywania informacji o tej pracy (być może wypełnionych przez frontend). Jeśli wykopiesz wystarczającą ilość małych partii i zużyjesz cały krzem, aby śledzić pracę, frontend będzie musiał się zatrzymać, nawet jeśli wokół będzie siedziało mnóstwo niewykorzystanej mocy. Oto zasada: im więcej zmian stanu (i małych losowań), tym większe prawdopodobieństwo głodu zaplecza GPU .
W trakcie przetwarzania losowania, po prostu uruchamiasz programy cieniujące, które robią dostęp do pamięci w celu pobrania twoich mundurów, twoich danych bufora wierzchołków, twoich tekstur, ale także struktur kontrolnych, które mówią jednostkom cieniującym, gdzie twoje bufory wierzchołków i twoje tekstury są. GPU ma również pamięci podręczne przed dostępem do pamięci. Dlatego za każdym razem, gdy rzucasz nowe mundury lub nowe powiązania tekstury / bufora na GPU, prawdopodobnie po pierwszym czytaniu będzie brakować pamięci podręcznej. Kolejna zasada: większość zmian stanu spowoduje brak pamięci podręcznej GPU. (Jest to najbardziej znaczące, gdy samodzielnie zarządzasz stałymi buforami: jeśli utrzymasz stałe bufory między losowaniami, to jest większe prawdopodobieństwo, że pozostaną one w pamięci podręcznej na GPU.)
Dużą część kosztów zmian stanu zasobów modułu cieniującego stanowi strona procesora. Ilekroć ustawiasz nowy stały bufor, sterownik najprawdopodobniej kopiuje zawartość tego stałego bufora do strumienia poleceń dla GPU. Jeśli ustawisz pojedynczy mundur, sterownik najprawdopodobniej przekształci go w duży stały bufor za twoimi plecami, więc musi sprawdzić przesunięcie tego munduru w stałym buforze, skopiować wartość do, a następnie zaznaczyć stały bufor jako brudny, aby mógł zostać skopiowany do strumienia poleceń przed następnym wywołaniem losowania. Jeśli powiążesz nowy bufor tekstury lub wierzchołka, sterownik prawdopodobnie kopiuje strukturę kontrolną dla tego zasobu. Ponadto, jeśli używasz osobnego procesora graficznego w wielozadaniowym systemie operacyjnym, sterownik musi śledzić każdy używany zasób i kiedy zaczynasz go używać, aby jądro „ Menedżer pamięci GPU może zagwarantować, że pamięć dla tego zasobu będzie rezydentna w VRAM GPU, gdy nastąpi losowanie. Zasada:zmiany stanu powodują, że sterownik przetasowuje pamięć, aby wygenerować minimalny strumień poleceń dla GPU.
Po zmianie bieżącego modułu cieniującego prawdopodobnie przyczyną jest brak pamięci podręcznej GPU (one również mają pamięć podręczną instrukcji!). Zasadniczo praca z procesorem powinna ograniczać się do wprowadzenia nowego polecenia do strumienia poleceń, mówiąc „użyj modułu cieniującego”. W rzeczywistości jest jednak cały bałagan kompilacji shaderów. Sterowniki GPU bardzo często leniwie kompilują shadery, nawet jeśli shadery zostały wcześniej utworzone. Bardziej istotne w tym temacie jednak niektóre stany nie są obsługiwane natywnie przez sprzęt GPU i zamiast tego są kompilowane w programie cieniującym. Jednym z popularnych przykładów są formaty wierzchołków: mogą być one wkompilowane w moduł cieniujący wierzchołki zamiast być osobnym stanem na chipie. Jeśli więc używasz formatów wierzchołków, których wcześniej nie używałeś z konkretnym modułem cieniującym wierzchołki, być może teraz płacisz sporo kosztów procesora za załatanie modułu cieniującego i skopiowanie programu modułu cieniującego do GPU. Dodatkowo, sterownik i kompilator modułu cieniującego może konspirować, aby robić różne rzeczy, aby zoptymalizować wykonanie programu modułu cieniującego. Może to oznaczać optymalizację układu pamięci mundurów i struktur kontroli zasobów, aby były ładnie zapakowane w sąsiednie rejestry pamięci lub modułu cieniującego. Tak więc, kiedy zmieniasz shadery, może to spowodować, że sterownik spojrzy na wszystko, co już związałeś z potokiem i przepakuje go w zupełnie innym formacie dla nowego modułu cieniującego, a następnie skopiuje to do strumienia poleceń. Zasada: Może to oznaczać optymalizację układu pamięci mundurów i struktur kontroli zasobów, aby były ładnie zapakowane w sąsiednie rejestry pamięci lub modułu cieniującego. Tak więc, kiedy zmieniasz shadery, może to spowodować, że sterownik spojrzy na wszystko, co już związałeś z potokiem i przepakuje go w zupełnie innym formacie dla nowego modułu cieniującego, a następnie skopiuje to do strumienia poleceń. Zasada: Może to oznaczać optymalizację układu pamięci mundurów i struktur kontroli zasobów, aby były ładnie zapakowane w sąsiednie rejestry pamięci lub modułu cieniującego. Tak więc, kiedy zmieniasz shadery, może to spowodować, że sterownik spojrzy na wszystko, co już związałeś z potokiem i przepakuje go w zupełnie innym formacie dla nowego modułu cieniującego, a następnie skopiuje to do strumienia poleceń. Zasada:zmiana shaderów może powodować tasowanie pamięci procesora.
Zmiany bufora ramki są prawdopodobnie najbardziej zależne od implementacji, ale generalnie są dość drogie na GPU. Twój procesor GPU może nie być w stanie obsłużyć wielu wywołań losowania do różnych celów renderowania jednocześnie, więc może być konieczne zatrzymanie potoku między tymi dwoma wywołaniami losowania. Może być konieczne opróżnienie pamięci podręcznej, aby cel renderowania mógł zostać odczytany później. Może być konieczne rozstrzygnięcie pracy odłożonej podczas rysowania. (Bardzo często gromadzi się oddzielną strukturę danych wraz z buforami głębokości, celami renderowania MSAA itp. Może to wymagać finalizacji po przełączeniu się z tego celu renderowania. Jeśli korzystasz z procesora graficznego opartego na kafelkach , podobnie jak wiele mobilnych procesorów graficznych, po przejściu z bufora ramki może być konieczne spłukanie dość dużej ilości faktycznych operacji cieniowania.) Zasada:zmiana celów renderowania jest kosztowna na GPU.
Jestem pewien, że to wszystko jest bardzo mylące i niestety trudno jest sprecyzować, ponieważ szczegóły często nie są jawne, ale mam nadzieję, że jest to całkiem przyzwoity przegląd niektórych rzeczy, które faktycznie dzieją się, gdy wywołujesz jakiś stan zmiana funkcji w twoim ulubionym graficznym interfejsie API.
źródło