Уникнути, якщо заяви в шейдерах DirectX 10?


14

Я чув, що якщо тверджень слід уникати в шейдерах, тому що обидві частини висловлювань будуть виконані, а тоді неправильне буде випадене (що шкодить продуктивності).

Це все ще проблема в DirectX 10? Хтось мені сказав, що в ньому буде виконуватися тільки правильна гілка.

Для ілюстрації у мене є код:

float y1 = 5; float y2 = 6; float b1 = 2; float b2 = 3;

if(x>0.5){
    x = 10 * y1 + b1;
}else{
    x = 10 * y2 + b2;
}

Чи є інший спосіб зробити це швидше?

Якщо так, то як це зробити?

Обидві гілки виглядають схоже, єдиною різницею є значення "константи" ( y1, y2, b1, b2однакові для всіх пікселів у Pixel Shader).


1
Чесно кажучи, це дуже передчасна оптимізація, просто не змінюйте їх, поки ви не орієнтуєте свій код і не на 100% почнете, що шейдер є вузьким місцем.
pwny

Відповіді:


17

Багато правил для мікрооптимізації шейдерів такі ж, як і для традиційних процесорів з векторними розширеннями. Ось кілька підказок:

  • є вбудовані тестові функції ( test, lerp/ mix)
  • додавання двох векторів має таку ж вартість, як і додавання двох плавців
  • шипіння безкоштовне

Це правда, що гілки дешевіші на сучасному обладнанні, ніж раніше, але все ж краще уникати їх, якщо це можливо. За допомогою функцій swizzling та тестування ви можете переписати шейдер без тестів:

/* y1, y2, b1, b2 */
float4 constants = float4(5, 6, 2, 3);

float2 tmp = 10 * constants.xy + constants.zw;
x = lerp(tmp[1], tmp[0], step(x, 0.5));

Використання stepі lerpє дуже поширеною фразою для вибору між двома значеннями.


6

Взагалі це нормально. Шейдери виконуватимуться в групах вершин або пікселів (різні постачальники мають різну термінологію для цих, тому я тримаюсь подалі від цього), і якщо всі вершини або пікселі в групі беруть один і той же шлях, то вартість розгалуження незначна.

Вам також потрібно довіритися компілятору шейдерів. Код HLSL, який ви пишете, не слід розглядати як безпосереднє представлення байтового коду або навіть збірки, до якого він буде компілюватися, і компілятор цілком вільний, щоб перетворити його на щось еквівалентне, але уникає гілки (наприклад, лерп іноді може бути краща конверсія). З іншого боку, якщо компілятор визначить, що виконання гілки насправді є більш швидким шляхом, він скомпілює її до гілки. Тут може бути дуже корисним перегляд створеної збірки в PIX або подібному інструменті.

Нарешті, стара мудрість все-таки зберігається тут - профіліруйте її, визначте, чи це насправді проблема продуктивності, і вирішіть її тоді, а не раніше. Якщо припустити, що щось може бути проблемою продуктивності, і діяти відповідно до цього припущення призведе лише до величезного ризику великих проблем згодом.


4

Цитата з посилання / статті, опублікованої Робертом Рухані:

"Коди умов (передбачення) використовуються в старих архітектурах для імітації справжнього розгалуження. Якщо-тоді заяви, складені для цих архітектур, повинні оцінювати як взяті, так і не взяті інструкції гілки на всі фрагменти. Стан гілки оцінюється та встановлюється код умови. інструкції в кожній частині гілки повинні перевірити значення коду умови перед тим, як записати їх результати в регістри. Як результат, лише вказівки у взятих гілках записують свої результати. Таким чином, у цих архітектурах всі гілки коштують стільки, скільки обидві частини відділення, плюс витрати на оцінку стану гілки. В таких архітектурах слід використовувати гілкування. Графічні процесори NVIDIA GeForce FX Series використовують емуляцію гілки умовного коду у своїх процесорах фрагментів. "

Як запропонував mh01 ("Перегляд згенерованої збірки в PIX або подібному інструменті тут може бути дуже корисним."), Ви повинні використовувати інструмент компілятора, щоб вивчити вихід. На мій досвід, інструмент Cg nVidia (Cg досі широко використовується завдяки можливостям крос-платформи) дав ідеальну ілюстрацію поведінки, згаданої в абзаці коду (передбачення) GPU gems . Таким чином, незалежно від значення тригера, обидві гілки оцінювались за кожним фрагментом, і лише наприкінці праву було розміщено у вихідному реєстрі. Тим не менш, час обчислень було витрачено даремно. Тоді я думав, що розгалуження допоможе продуктивність, тим більше, що всіфрагменти в тій шейдері покладалися на рівномірне значення для вирішення правильної гілки - це сталося не за призначенням. Отже, тут є головне застереження (наприклад, уникайте шахраїв - можливо, найбільше джерело пекло розгалуження).


2

Якщо у вас вже не виникають проблеми з продуктивністю, це добре. Витрати на порівняння проти константи все ще надзвичайно дешеві. Ось добре прочитайте про розгалуження GPU: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter34.html

Незважаючи на те, ось фрагмент коду, який буде готувати набагато гірше, ніж оператор if (і набагато менш читабельний / піддається ремонту), але все ж позбавляється від нього:

int fx = floor(x);
int y = (fx * y2) + ((1- fx) * y1);
int b = (fx * b2) + ((1 -fx) * b1);

x = 10 * y + b;

Зауважте, що я роблю припущення, що х обмежений діапазоном [0, 1]. Це не спрацює, якщо x> = 2 або x <0.

Те, що зроблено, - це перетворити х на або 0або 1помножити неправильний на 0, а інший на 1.


Оскільки в оригінальному тесті if(x<0.5)значення fxмає бути round(x)або floor(x + 0.5).
sam hocevar

1

Є кілька інструкцій, здатних виконувати умови без розгалуження;

vec4 when_eq(vec4 x, vec4 y) {
  return 1.0 - abs(sign(x - y));
}

vec4 when_neq(vec4 x, vec4 y) {
  return abs(sign(x - y));
}

vec4 when_gt(vec4 x, vec4 y) {
  return max(sign(x - y), 0.0);
}

vec4 when_lt(vec4 x, vec4 y) {
  return max(sign(y - x), 0.0);
}

vec4 when_ge(vec4 x, vec4 y) {
  return 1.0 - when_lt(x, y);
}

vec4 when_le(vec4 x, vec4 y) {
  return 1.0 - when_gt(x, y);
}

Плюс деякі логічні оператори;

vec4 and(vec4 a, vec4 b) {
  return a * b;
}

vec4 or(vec4 a, vec4 b) {
  return min(a + b, 1.0);
}

vec4 xor(vec4 a, vec4 b) {
  return (a + b) % 2.0;
}

vec4 not(vec4 a) {
  return 1.0 - a;
}

джерело: http://theorangeduck.com/page/avoiding-shader-conditionals

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.