2010-11-14 15 views
35

Rozumiem, że to pytanie może wydawać się nieco nieuzasadnione, ale , jeśli ktoś wie coś teoretycznego/ma praktyczne doświadczenie na ten temat, byłoby wspaniale, gdybyś je udostępnił.Efektywność rozgałęzienia w shaderach

Próbuję zoptymalizować jeden z moich starych shaderów, który korzysta z wielu wyszukiwań tekstur.

Mam rozproszonych, normalny, lustrzane Maps dla każdego z trzech możliwych płaszczyznach mapowania i dla niektórych twarzach znajdujących się w pobliżu użytkownika Mam też zastosować techniki mapowania, które również przynieść dużo tekstur wyszukiwań (jak parallax occlusion mapping).

Profilowanie pokazało, że szukanie tekstury jest wąskim gardłem shadera i jestem gotów je usunąć. Dla niektóre przypadki wejścia Parametry już wiem, że część wyszukiwań tekstur byłoby zbędne i oczywiste rozwiązaniem jest zrobić coś takiego (Pseudokod):

if (part_actually_needed) { 
    perform lookups; 
    perform other steps specific for THIS PART; 
} 

// All other parts. 

Teraz - tu chodzi pytanie.

nie pamiętam dokładnie (dlatego stwierdziłem pytanie może być nieuziemiona), ale w jakiś papier Niedawno przeczytałem (niestety nie pamiętam nazwy) coś podobnego do poniższego stwierdzono :

występ prezentowanego techniki zależy od tego, jak wydajny sprzętowe WARUNKOWA rozgałęzienia jest zaimplementowany.

pamiętałem tego rodzaju oświadczenia tuż przed miałem rozpocząć refactoring dużą liczbę shaderów i wdrożyć if opartych optymalizacji mi chodzi.

Tak - zanim zacznę to robić - czy ktoś wie coś o wydajności rozgałęzień w shaderów? Dlaczego rozgałęzienia mogą powodować surowe kary za wydajność w shaderów?

Czy jest możliwe, że mogłem tylko pogorszyć rzeczywistą wydajność dzięki rozgałęzieniu opartemu na if?


Można powiedzieć - spróbować i zobaczyć. Tak, właśnie to mam zamiar zrobić, jeśli nikt mi tu nie pomoże.

Ale nadal, co w przypadku if może być skuteczne dla nowych GPU, może być koszmarem dla nieco starszych.I tego rodzaju kwestii jest bardzo trudne do przewidzenia, chyba że masz wiele różnych GPU (to nie moja sprawa)

Tak więc, jeśli ktoś wie coś o tym czy ma doświadczenie benchmarkingu dla tego rodzaju cieniowania, ja byłbym wdzięczny za twoją pomoc.


nieliczne pozostałe komórki mózgowe, które są rzeczywiście działa powtarzają mi, że rozgałęzienia na GPU może być daleko nie tak skuteczne, jak rozgałęzienia dla procesora (co zwykle ma niezwykle skutecznych sposobów przewidywania branży oraz wyeliminowanie sytuacji niebezpiecznych cache) tylko dlatego, że jest to procesor graficzny (lub taki, który może być trudny do wdrożenia na GPU).

Niestety nie jestem pewien, czy to stwierdzenie nie ma nic wspólnego z rzeczywistą sytuacją ...

+1

Wybacz; ale co C++ ma wspólnego z shaderów? – zneak

+1

Mimo to możesz chcieć nadać swoim tematom nieco bardziej opisowy tytuł. Już widzę 4 tematy, które utworzyłeś już pod tym samym tytułem, ale inne pytanie. – Bart

Odpowiedz

27

Niestety, uważam, że prawdziwą odpowiedzią jest wykonanie praktycznego testu z analizatorem wydajności w konkretnym przypadku na docelowym sprzęcie. Szczególnie, że brzmi to jak na etapie optymalizacji projektu; jest to jedyny sposób, aby wziąć pod uwagę fakt, że sprzęt zmienia się często i charakter konkretnego modułu cieniującego.

W przypadku procesora, jeśli otrzymasz błędnie wykrytą gałąź, spowodujesz spłukiwanie rurociągu, a ponieważ potoki CPU są tak głębokie, możesz skutecznie stracić coś w kolejności 20 lub więcej cykli. Na GPU rzeczy trochę się różnią; potok jest prawdopodobnie znacznie płytszy, ale nie ma przewidywania rozgałęzień, a cały kod modułu cieniującego będzie w szybkiej pamięci - ale to nie jest prawdziwa różnica.

Trudno jest poznać dokładne szczegóły wszystkiego, co się dzieje, ponieważ nVidia i ATI mają stosunkowo wąskie wargi, ale kluczowe jest to, że układy GPU są wykonywane masowo równolegle. Istnieje wiele asynchronicznych rdzeni cieniowania, ale każdy rdzeń jest ponownie zaprojektowany do uruchamiania wielu wątków. Rozumiem, że każdy rdzeń oczekuje uruchomienia tej samej instrukcji na wszystkich wątkach w danym cyklu (nVidia nazywa tę kolekcję wątków "osnową").

W tym przypadku wątek może reprezentować wierzchołek, element geometrii lub piksel/fragment, a osnowa to zbiór około 32 z nich. W przypadku pikseli najprawdopodobniej będą to piksele, które są blisko siebie na ekranie. Problem polega na tym, że jeśli w obrębie jednej osnowy różne wątki podejmują różne decyzje przy warunkowym skoku, osnowa oddzieliła się i nie uruchamia już tej samej instrukcji dla każdego wątku. Sprzęt może sobie z tym poradzić, ale nie jest do końca jasne (przynajmniej dla mnie), jak to robi. Jest również prawdopodobne, że będzie traktowany nieco inaczej dla każdej kolejnej generacji kart.Najnowszy, najbardziej ogólny NVidias przyjazny dla CUDA/Compute-Shader może mieć najlepszą implementację; starsze karty mogą mieć gorszą implementację. Najgorsze jest to, że możesz znaleźć wiele wątków wykonujących obie strony instrukcji if/else.

Jedną z wielkich sztuczek z modułami cieniującymi jest nauka wykorzystania tego masywnie równoległego paradygmatu. Czasami oznacza to użycie dodatkowych przejść, tymczasowych buforów offscreenowych i buforów szablonów w celu wypchnięcia logiki z shaderów i na procesor. Czasami może się wydawać, że optymalizacja powoduje więcej cykli, ale może zmniejszyć niektóre ukryte koszty.

Należy również zauważyć, że można wyraźnie zaznaczyć, czy instrukcje w modułach cieniujących DirectX mają postać [gałąź] lub [spłaszcz]. Styl spłaszczania daje właściwy rezultat, ale zawsze wykonuje wszystkie instrukcje. Jeśli nie wybierzesz jednoznacznie, kompilator może wybrać dla ciebie - i może wybrać [spłaszcz], co nie jest dobre dla twojego przykładu.

Jedną rzeczą do zapamiętania jest to, że jeśli przeskoczyć pierwszego tekstury odnośnika, to będzie mylić tekstury sprzęcie za koordynowanie matematyki pochodnych. Dostaniesz błędy kompilatora i najlepiej tego nie robić, w przeciwnym razie możesz stracić trochę lepszej obsługi teksturowania.

6

nie wiem o optymalizacje jeśli opartych na, ale jak tylko o stworzenie wszystkie permutacje szukanie tekstur, które uważasz za potrzebne, każdy z nich posiada własny moduł cieniujący i po prostu użyj właściwego modułu cieniującego w odpowiedniej sytuacji (w zależności od tego, która tekstura jest wymagana dla danego modelu lub części modelu). Myślę, że zrobiliśmy coś takiego na Bully na konsolę Xbox 360.

28

Jeśli warunek jest jednolity (tj. Stały dla całego przebiegu), to gałąź jest zasadniczo wolna, ponieważ struktura zasadniczo skompiluje dwie wersje modułu cieniującego (gałąź podjęte i nie) i wybierz jedną z nich dla całego przebiegu na podstawie zmiennej wejściowej. W takim przypadku, zdecydowanie przejdź do oświadczenia if, ponieważ będzie uczynić swój moduł cieniujący szybciej.

Jeśli warunek jest różny dla każdego wierzchołka/piksela, może to w rzeczywistości pogorszyć wydajność, a starsze modele modułu cieniującego nawet nie obsługują dynamicznego rozgałęzienia.

+17

To jest bardzo interesujące. Czy istnieje dokument, który to potwierdza? – Yury

+0

@Jak obejrzeć np. [GL_ARB_fragment_program] (https://www.opengl.org/registry/specs/ARB/fragment_program.txt), który jest jednym z pierwszych rozszerzeń obsługi modułów cieniujących dla OpenGL. Tam jedyną instrukcją warunkową jest 'CMP', która jest bardziej podobna do instrukcji x86' CMOVcc' - ruch warunkowy. A w podobnym rozszerzeniu wsparcia dla modułów cieniujących w vertexie brakuje nawet 'CMP'. To pokazuje, że GPU w tym czasie (rozszerzenie zostało zatwierdzone w 2002 r.) Nie obsługuje w ogóle prawdziwego rozgałęzienia. – Ruslan

10

Oto testów wydajności rzeczywistego świata na Kindle Fire:

W cieniującego fragmentu ...

ten biegnie na 20fps:

lowp vec4 a = vec4(0.0, 0.0, 0.0, 0.0); 
if (a.r == 0.0) 
    gl_FragColor = texture2D (texture1, TextureCoordOut); 

ten biegnie na 60fps:

gl_FragColor = texture2D (texture1, TextureCoordOut); 
+9

Czy to naprawdę kompletny kod? – Tara

23

W wielu przypadkach obie gałęzie mogą być obliczane i mieszane według warunku jako olator. To podejście działa znacznie szybciej niż rozgałęzienie. Może być również użyty na procesorze. Na przykład:

...

vec3 c = vec3(1.0, 0.0, 0.0); if (a == b) c = vec3(0.0, 1.0, 0.0);

może być zastąpiony przez:

vec3 c = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), (a == b));

...

+0

Lub użyj operatora trójskładnikowego! ;) Nie wierzę, że jest traktowane tak samo jak oświadczenie if. –

+0

Nie. Operator trójskładnikowy jest taki sam, jak w przypadku. Lepiej wykorzystaj miks – vec3h

Powiązane problemy