Dlaczego bezpieczeństwo wątków jest tak wielką sprawą dla interfejsów API grafiki?

21

Zarówno Vulkan, jak i DirectX12 mają być użyteczne w sposób bezpieczny dla wątków. Ludzie wydają się być tym podekscytowani.

Dlaczego jest to tak ogromna funkcja? „Rzeczywiste” przetwarzanie i tak zostaje przerzucone przez most pamięci na oddzielnej jednostce przetwarzającej.

Także jeśli jest tak duży, dlaczego nie pojawił się do tej pory bezpieczny wątkowy interfejs API grafiki?

maniak zapadkowy
źródło
Ten artykuł jest o wiele bardziej „skupiony na graczach
glampert

Odpowiedzi:

13

Główną korzyścią byłoby to, że łatwiej byłoby podzielić zadania procesora na wiele wątków, bez konieczności rozwiązywania wszystkich trudnych problemów z dostępem do graficznego interfejsu API. Zwykle albo trzeba zaktualizować kontekst (co może mieć zły wpływ na wydajność), albo podać kolejkę i wywołać interfejs graficzny w jednym wątku. Nie sądzę, aby w ten sposób osiągnięto jakąkolwiek wydajność, ponieważ GPU i tak faktycznie przetwarza je sekwencyjnie, ale znacznie ułatwia pracę programistom.

Przyczyną tego, że do tej pory tego nie robiono, jest prawdopodobnie to, że directx i opengl powstały w czasach, gdy wielowątkowość nie była tak naprawdę widoczna. Również tablica Khronos jest bardzo konserwatywna w zakresie zmiany interfejsu API. Ich pogląd na Vulkan jest taki, że będzie on współistniał obok OpenGL, ponieważ oba służą innym celom. Prawdopodobnie dopiero niedawno paraliż stał się tak ważny, ponieważ konsumenci mają dostęp do coraz większej liczby procesorów.

EDYCJA: Nie chodzi mi o to, że praca nie jest osiągana na wielu procesorach, nie jest przydatne dzielenie połączeń na wiele wątków, aby szybciej tworzyć tekstury / shadery. Wydajność jest raczej uzyskiwana dzięki większej liczbie procesorów zajętych i utrzymywaniu procesora przez procesory.

Maurice Laveaux
źródło
1
Jako dodatkowa uwaga, OpenGL zasadniczo działa tylko na jednym wątku, więc aplikacja intensywnie korzystająca z grafiki może maksymalnie wykorzystać jeden rdzeń. Coś takiego jak Vulkan umożliwia wysyłanie poleceń do kolejki przez wiele wątków, co oznacza, że ​​wiele połączeń graficznych można wykonać z wielu wątków.
Soapy
9

Procesor potrzebuje dużo pracy, aby skonfigurować ramkę dla GPU, a spora część tej pracy znajduje się w sterowniku graficznym. Przed DX12 / Vulkan praca sterownika grafiki była zasadniczo zmuszona do jednowątkowego projektowania interfejsu API.

Mamy nadzieję, że DX12 / Vulkan zniesie to ograniczenie, umożliwiając równoległą pracę sterownika na wielu wątkach procesora w ramce. Umożliwi to bardziej wydajne wykorzystanie procesorów wielordzeniowych, umożliwiając silnikom gier wypychanie bardziej złożonych scen bez wiązania się z procesorem. To jest nadzieja - czy to zostanie zrealizowane w praktyce, będziemy musieli poczekać w ciągu najbliższych kilku lat.

Aby nieco rozwinąć: wyjście mechanizmu renderującego silnik gry jest strumieniem wywołań interfejsu API DX / GL, które opisują sekwencję operacji renderowania ramki. Istnieje jednak duża odległość między strumieniem wywołań interfejsu API a faktycznymi buforami poleceń binarnych zużywanymi przez sprzęt GPU. Sterownik musi „skompilować” wywołania API na język maszynowy GPU, że tak powiem. To nie jest trywialny proces - wymaga wielu tłumaczeń pojęć API na rzeczywistość sprzętową niskiego poziomu, sprawdzania poprawności, aby upewnić się, że procesor graficzny nigdy nie jest ustawiony w nieprawidłowy stan, zawirowania przydziałów pamięci i danych, śledzenia zmian stanu w celu wydania popraw polecenia niskiego poziomu i tak dalej. Sterownik graficzny jest odpowiedzialny za wszystkie te rzeczy.

W interfejsach API DX11 / GL4 i wcześniejszych praca ta jest zwykle wykonywana przez wątek jednego sterownika. Nawet jeśli wywołasz interfejs API z wielu wątków (co możesz zrobić na przykład przy użyciu listy odroczonych poleceń DX11), to po prostu dodaje trochę pracy do kolejki, aby wątek sterownika mógł przejrzeć później. Jednym z głównych powodów tego jest śledzenie stanu, o którym wspomniałem wcześniej. Wiele szczegółów konfiguracji GPU na poziomie sprzętowym wymaga znajomości aktualnego stanu potoku graficznego, więc nie ma dobrego sposobu na podzielenie listy poleceń na części, które mogą być przetwarzane równolegle - każda część musiałaby dokładnie wiedzieć, w jakim stanie powinna rozpocząć z, chociaż poprzedni fragment nie został jeszcze przetworzony.

To jedna z wielkich rzeczy, które zmieniły się w DX12 / Vulkan. Po pierwsze, zawierają prawie cały stan potoku graficznego w jednym obiekcie, a po drugie (przynajmniej w DX12), kiedy zaczynasz tworzyć listę poleceń, musisz podać początkowy stan potoku; stan nie jest dziedziczony z jednej listy poleceń do następnej. Zasadniczo pozwala to kierowcy nie wiedzieć nic o poprzednich listach poleceń przed rozpoczęciem kompilacji - a to z kolei pozwala aplikacji na rozbicie renderowania na równoległe fragmenty, tworząc w pełni skompilowane listy poleceń, które można następnie połączone razem i wysłane do GPU przy minimalnym wysiłku.

Oczywiście istnieje wiele innych zmian w nowych interfejsach API, ale jeśli chodzi o wielowątkowość, jest to najważniejsza część.

Nathan Reed
źródło
5

Nowoczesne procesory graficzne mają na ogół jedną sekcję interfejsu, która przetwarza całkowicie liniowy strumień poleceń z procesora. To, czy jest to naturalny projekt sprzętu, czy po prostu ewoluowało z czasów, gdy istniał pojedynczy rdzeń procesora generujący polecenia dla GPU, jest dyskusyjne, ale na razie tak jest. Więc jeśli wygenerujesz pojedynczy liniowy strumień poleceń stanowych, oczywiście sensowne jest wygenerowanie tego strumienia liniowo w jednym wątku procesora! Dobrze?

Cóż, współczesne procesory graficzne mają również na ogół bardzo elastyczny zunifikowany backend, który może pracować na wielu różnych rzeczach jednocześnie. Ogólnie rzecz biorąc, GPU działa na wierzchołkach i pikselach z dość drobną ziarnistością. Nie ma dużej różnicy między GPU przetwarzającym 1024 wierzchołki w jednym losowaniu i 512 + 512 wierzchołków w dwóch różnych losowaniach.

To sugeruje dość naturalny sposób na wykonanie mniejszej pracy: zamiast rzucać dużą liczbą wierzchołków w GPU w jednym wywołaniu losowania, podziel model na sekcje, wykonaj tanie gruboziarniste ubijanie na tych sekcjach i prześlij każdą porcję indywidualnie, jeśli przejdzie test uboju. Jeśli zrobisz to z odpowiednią dokładnością, powinieneś uzyskać niezłe przyspieszenie!

Niestety, w obecnej rzeczywistości graficznego interfejsu API, wywołania są bardzo kosztowne dla procesora. Uproszczone wyjaśnienie, dlaczego: zmiany stanu na GPU mogą nie odpowiadać bezpośrednio wywołaniom graficznego interfejsu API, więc wiele wywołań graficznego interfejsu API po prostu ustawia stan wewnątrz sterownika, a wywołanie losowania, które byłoby zależne od tego nowego stanu, przechodzi i sprawdza wszystkie stan oznaczony jako zmieniający się od ostatniego losowania zapisuje go w strumieniu poleceń GPU, a następnie inicjuje losowanie. To jest cała praca wykonywana w celu uzyskania ubogiego i średniego strumienia poleceń dla jednostki frontendowej GPU.

Sprowadza się to do tego, że masz budżet na losowanie połączeń, który jest całkowicie narzucony przez kierowcę . (Wydaje mi się, że słyszałem, że w dzisiejszych czasach można uzyskać około 5000 na klatkę za tytuł 60 klatek na sekundę). Możesz to zwiększyć o duży procent, budując ten strumień poleceń w równoległych porcjach.

Są też inne powody (na przykład asynchroniczne opóźnienie czasu w celu poprawy opóźnień VR), ale jest to duży problem w przypadku gier związanych z grafiką i innego oprogramowania wymagającego dużego zainteresowania (np. Pakietów do modelowania 3D).

John Calsbeek
źródło