Ответ @Stephane Hockenhull в значительной степени дает вам то, что вам нужно знать, он будет полностью зависеть от аппаратного обеспечения.
Но позвольте мне привести несколько примеров того, как это может быть зависят от аппаратного обеспечения, и поэтому ветвление даже проблема вообще, то , что делает GPU делает за кулисами , когда ветвление делает место.
Я сфокусирован в основном на Nvidia, у меня есть некоторый опыт программирования на низком уровне CUDA, и я вижу, что такое PTX ( IR для ядер CUDA) генерируется , например, SPIR-V, но только для Nvidia), и вижу критерии для внесения определенных изменений.
Почему ветвление в архитектуре GPU так важно?
Почему плохо ветвиться в первую очередь? Почему графические процессоры пытаются избежать ветвления в первую очередь? Поскольку графические процессоры обычно используют схему, в которой потоки используют один и тот же указатель команд . Графические процессоры следуют архитектуре SIMDкак правило, и хотя степень детализации этого может измениться (то есть 32 потока для Nvidia, 64 для AMD и других), на некотором уровне группа потоков совместно использует один и тот же указатель команд. Это означает, что эти потоки должны смотреть на одну и ту же строку кода, чтобы работать вместе над одной и той же проблемой. Вы можете спросить, как они могут использовать одни и те же строки кода и делать разные вещи? Они используют разные значения в регистрах, но эти регистры все еще используются в одних и тех же строках кода во всей группе. Что происходит, когда это перестает быть так? (IE ветвь?) Если программа действительно не может обойти ее, она разделяет группу (Nvidia, такие связки из 32 потоков называются деформацией , для AMD и академий параллельных вычислений она называется волновым фронтом ) на две или более разных групп.
Если есть только две разные строки кода, на которых вы в конечном итоге окажетесь, тогда рабочие потоки будут разделены на две группы (отсюда одну я буду называть их перекосами). Предположим, архитектура Nvidia, где размер деформации составляет 32, если половина этих потоков расходится, то у вас будет 2 деформации, занятые 32 активными потоками, что делает вдвое менее эффективными вычисления от конца до конца. На многих архитектурах GPU будет пытаться исправить это, объединяя потоки обратно в одну деформацию, как только они достигнут одной и той же ветки постов инструкций, или компилятор явно установит точку синхронизации, которая говорит GPU сходить потоки назад или попытаться это сделать.
например:
if(a)
x += z * w;
q >>= p;
else if(c)
y -= 3;
r += t;
Поток может сильно расходиться (разные пути команд), поэтому в таком случае может произойти конвергенция, r += t;
когда указатели команд снова будут одинаковыми. Расхождение также может происходить с более чем двумя ветвями, что приводит к еще более низкому использованию деформации, четыре ветви означают, что 32 потока разделены на 4 деформации, 25% -ная пропускная способность. Однако конвергенция может скрыть некоторые из этих проблем, поскольку 25% не сохраняют пропускную способность всей программы.
На менее сложных графических процессорах могут возникнуть другие проблемы. Вместо того, чтобы расходиться, они просто вычисляют все ветви, а затем выбирают результат в конце. Это может выглядеть так же, как расхождение (оба имеют пропускную способность 1 / n), но есть несколько основных проблем с подходом дублирования.
Одним из них является энергопотребление, когда вы используете ветку, вы используете гораздо больше энергии, это было бы плохо для мобильных графических процессоров. Во-вторых, расхождение происходит на GPU Nvidia только тогда, когда потоки одной и той же основы идут по разным путям и, таким образом, имеют другой указатель инструкций (который используется совместно с pascal). Таким образом, вы все равно можете иметь разветвление и не иметь проблем с пропускной способностью на графических процессорах Nvidia, если они встречаются кратными 32 или возникают только в одной деформации из десятков. если вероятнее всего произойдет ветвление, более вероятно, что меньше потоков будет расходиться, и у вас все равно не возникнет проблема ветвления.
Еще одна меньшая проблема - когда вы сравниваете GPU с CPU, у них часто нет механизмов прогнозирования и других надежных механизмов ветвления, поскольку из-за того, сколько аппаратного обеспечения занимает этот механизм, из-за этого часто можно увидеть заполнение без операций на современных GPU.
Практический пример архитектурной разницы графического процессора
Теперь давайте возьмем пример Стефана и посмотрим, как будет выглядеть сборка для решений без ветвей на двух теоретических архитектурах.
n = (a==b) ? x : y;
Как сказал Стефан, когда компилятор устройства встречает ветвь, он может решить использовать инструкцию, чтобы «выбрать» элемент, который в итоге не будет иметь штраф за ветвление. Это означает, что на некоторых устройствах это будет скомпилировано в нечто вроде
cmpeq rega, regb
// implicit setting of comparison bit used in next part
choose regn, regx, regy
на других без инструкции выбора, она может быть скомпилирована в
n = ((a==b))* x + (!(a==b))* y
который может выглядеть так:
cmpeq rega regb
// implicit setting of comparison bit used in next part
mul regn regcmp regx
xor regcmp regcmp 1
mul regresult regcmp regy
mul regn regn regresult
который без ветвей и эквивалентен, но требует больше инструкций. Поскольку пример Стефана, скорее всего, будет скомпилирован с любой из них на их соответствующих системах, не имеет особого смысла пытаться вручную вычислить математику для удаления ветвления, поскольку компилятор первой архитектуры может решить компилировать во вторую форму вместо более быстрая форма.