Czy if-statements Spowalnia mój shader?

Chcę wiedzieć, czy "if-statements" wewnątrz shaderów (wierzchołek / fragment / piksel...) naprawdę spowalniają działanie shader ' a. Na przykład:

Czy lepiej użyć tego:

vec3 output;
output = input*enable + input2*(1-enable);

Zamiast tego:

vec3 output;
if(enable == 1)
{
    output = input;
}
else
{
    output = input2;
}

Na innym forum była dyskusja o tym (2013): http://answers.unity3d.com/questions/442688/shader-if-else-performance.html Tutaj chłopaki mówią, że oświadczenia If są naprawdę złe dla wydajności shader.

Również tutaj mówią o tym, ile jest wewnątrz deklaracji if/else (2012): https://www.opengl.org/discussion_boards/showthread.php/177762-Performance-alternative-for-if-(-)

Może Sprzęt lub shadercompiler są teraz lepsze i jakoś naprawiają ten (może nie istniejący) problem z wydajnością.

EDIT:

Co jest w tym przypadku, powiedzmy, że enable jest zmienną jednorodną i jest zawsze ustawiona na 0:

if(enable == 1) //never happens
{
    output = vec4(0,0,0,0);
}
else  //always happens
{
    output = calcPhong(normal, lightDir);
}

Myślę, że tutaj mamy gałąź wewnątrz shadera, która spowalnia shadera. Zgadza się?

Czy bardziej sensowne jest tworzenie dwóch różnych shaderów, takich jak jeden dla else, a drugi dla if?

Author: Thomas, 2016-06-15

2 answers

Co jest takiego w shaderach, że potencjalnie powodują problemy z wydajnością if? Ma to związek z tym, jak shadery są wykonywane i skąd GPU uzyskać ich ogromną wydajność obliczeniową.

Oddzielne wywołania shadera są zwykle wykonywane w kolejności, wykonując te same instrukcje w tym samym czasie. Po prostu wykonują je na różnych zestawach wartości wejściowych; mają wspólne uniformy, ale mają różne wewnętrzne zmienne. Jeden termin dla grupy shaderów wszystkich wykonywanie tej samej sekwencji operacji to "wavefront".

Potencjalny problem z dowolną formą rozgałęzienia warunkowego polega na tym, że może to wszystko zepsuć. Powoduje to, że różne wywołania wewnątrz fali muszą wykonywać różne sekwencje kodu. Jest to bardzo kosztowny proces, w którym należy utworzyć nową falę, skopiować do niej dane itp. Chyba że... nie ma.

Na przykład, jeśli warunek jest taki, który jest przyjmowany przez co wywołanie w wavefront, wtedy nie jest potrzebna rozbieżność w uruchomieniu. Jako taki, koszt if jest tylko kosztem sprawdzenia warunku.

Jest kilka przypadków, które reprezentują wygląd Twojego kodu dla kompilatora:

  • rozgałęzianie statyczne. Warunek jest oparty na stałych w czasie kompilacji; jako taki, wiesz patrząc na kod i wiesz, które gałęzie będą brane. Prawie każdy kompilator obsługuje to jako część basic optymalizacja.
  • statycznie jednolite rozgałęzienia. Warunek jest oparty na wyrażeniach zawierających tylko uniformy lub stałe. Nie możesz a priori wiedzieć, która gałąź zostanie pobrana, ale kompilator może statycznie być pewien, że wavefronty nigdy nie zostaną złamane przez to if.
  • dynamiczne rozgałęzianie. Warunek jest oparty na wyrażeniach, które zawierają więcej niż tylko stałe i uniformy. W tym przypadku kompilator nie może a priori stwierdzić, czy pasmo falowe zostanie zerwane, czy nie. Czy to się stanie zależy od runtime ewaluacji wyrażenia warunkowego.

Różne urządzenia mogą obsługiwać różne typy rozgałęzień bez rozbieżności.

Ponadto, nawet jeśli warunek jest przyjmowany przez różne pasma falowe, można zrestrukturyzować kod tak, aby nie wymagał rzeczywistego rozgałęzienia . Podałeś dobry przykład: output = input*enable + input2*(1-enable); jest funkcjonalnie równoważne instrukcji if. Kompilator może wykryć, że if jest używany do ustawiania zmiennej, a tym samym wykonać oba boki. Jest to często wykonywane w przypadkach, gdy sprzęt nie może stwierdzić, czy warunek może być wykonany bez rozbieżności, ale ciała tych dwóch warunków są małe.

Prawie każdy sprzęt może obsłużyć var = bool ? val1 : val2 bez konieczności rozchodzenia się. Było to możliwe w 2002 roku.

Ponieważ jest to bardzo zależne od sprzętu, it... zależy od sprzętu. Istnieją jednak pewne epoki sprzętu, na które można spojrzeć:

Desktop, Pre-D3D10

Tam, to taki dziki zachód. Kompilator NVIDII dla takiego sprzętu był znany z wykrywania takich warunków i w rzeczywistości rekompilowania shadera za każdym razem, gdy zmieniałeś mundury, które miały wpływ na takie warunki.

Ogólnie rzecz biorąc, w tej erze pochodzi około 80% "nigdy nie używaj if oświadczeń". Ale nawet tutaj, to niekoniecznie prawda.

Można oczekiwać optymalizacji rozgałęzień statycznych. Możesz mieć nadzieję że statycznie jednolite rozgałęzienia nie spowodują żadnych dodatkowe spowolnienie (choć fakt, że NVIDIA uważała, że rekompilacja będzie szybsza niż jej wykonanie sprawia, że jest to mało prawdopodobne przynajmniej dla ich sprzętu). Ale dynamiczne rozgałęzienie będzie cię coś kosztować, nawet jeśli wszystkie wywołania będą miały tę samą gałąź.

Kompilatory tej epoki dokładają wszelkich starań, aby zoptymalizować shadery, tak aby proste warunki mogły być wykonane w prosty sposób. Na przykład twój output = input*enable + input2*(1-enable); jest czymś, co porządny kompilator mógłby wygenerować z twojego odpowiednika if oświadczenie.

Desktop, Post-D3D10

Sprzęt tej epoki jest na ogół w stanie obsługiwać statycznie jednolite deklaracje gałęzi z niewielkim spowolnieniem. W przypadku dynamicznego rozgałęziania możesz napotkać spowolnienie.

Pulpit, d3d11 +

Sprzęt tej epoki jest w zasadzie gwarantowany, że będzie w stanie obsłużyć dynamicznie jednolite Warunki z niewielkimi problemami z wydajnością. Rzeczywiście, nawet nie musi być dynamicznie jednorodny; tak długo, jak wszystkie wywołania w tej samej linii falowej podążają tą samą ścieżką, nie zauważysz żadnej znaczącej utraty wydajności.

Zauważ, że niektóre urządzenia z poprzedniej epoki prawdopodobnie mogłyby to zrobić. Ale to jest ten, w którym jest prawie pewne, że to prawda.

Mobile, ES 2.0

Witamy z powrotem na Dzikim Zachodzie. Chociaż w przeciwieństwie do pulpitu Pre-D3D10, wynika to głównie z ogromnej wariancji sprzętu ES 2.0. Jest tak ogromna ilość rzeczy, które poradzą sobie z ES 2.0, a oni wszystkie działają bardzo inaczej od siebie.

Rozgałęzienia statyczne prawdopodobnie zostaną zoptymalizowane. Ale to, czy uzyskasz dobrą wydajność dzięki statycznie jednolitemu rozgałęzieniu, jest bardzo zależne od sprzętu.

Mobile, ES 3.0 +

Tutaj sprzęt jest raczej dojrzalszy i bardziej wydajny niż ES 2.0. W związku z tym można oczekiwać, że statycznie jednolite gałęzie będą działać w miarę dobrze. A niektóre urządzenia mogą prawdopodobnie obsługiwać dynamiczne gałęzie tak, jak robi to nowoczesny sprzęt komputerowy.

 69
Author: Nicol Bolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-22 15:52:27

Jest wysoce zależny od sprzętu i stanu.

Jeśli twój warunek jest jednolity: nie kłopocz się, pozwól kompilatorowi się tym zająć. Jeśli twój warunek jest czymś dynamicznym (jak wartość obliczona z atrybutu lub pobrana z tekstury lub czegoś takiego), to jest to bardziej skomplikowane.

W tym drugim przypadku będziesz musiał przetestować i porównać, ponieważ będzie to zależało od złożoności kodu w każdej gałęzi i od tego, jak "spójna" jest decyzja oddziału jest.

Na przykład, jeśli jedna z gałęzi jest brana w 99% przypadków i odrzuca fragment, to najprawdopodobniej chcesz zachować warunek. Ale OTOH w Twoim prostym przykładzie powyżej jeśli enable jest pewnym warunkiem dynamicznym, wybór arytmetyczny może być lepszy.

Jeśli nie masz jasnego przypadku, jak powyżej, lub jeśli nie optymalizujesz dla jednej ustalonej znanej architektury, prawdopodobnie lepiej będzie, gdy kompilator się o tym dowie.

 5
Author: 246tNt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-15 06:18:34