Филиал и предикатные инструкции

Раздел 5.4.2 Руководства по программированию CUDA C гласит, что ветвь расходимости обрабатывается либо "инструкциями ветвления", либо, при определенных условиях, "предварительные инструкции". Я не понимаю разницы между ними и почему один ведет к лучшей производительности, чем другой.

Этот комментарий предполагает, что инструкции ветки приводят к большему количеству выполненных инструкций, застопориванию из-за "разрешения адреса ветки и выборки" и накладных расходов из-за "ветки сам" и "бухгалтерский учет для расхождения", в то время как в заданных инструкциях берется только "латентность выполнения команды для выполнения теста условия и установки предиката". Почему?

Ответ 1

Предикация команды означает, что команда условно выполняется потоком в зависимости от предиката. Темы, для которых предикат является истинным, выполняют инструкцию, остальные ничего не делают.

Например:

var = 0;

// Not taken by all threads
if (condition) {
    var = 1;
} else {
    var = 2;
}

output = var;

Результат (не фактический вывод компилятора):

       mov.s32 var, 0;       // Executed by all threads.
       setp pred, condition; // Executed by all threads, sets predicate.

@pred  mov.s32 var, 1;       // Executed only by threads where pred is true.
@!pred mov.s32 var, 2;       // Executed only by threads where pred is false.
       mov.s32 output, var;  // Executed by all threads.

В целом, это 3 инструкции для if, без ветвления. Очень эффективен.

Эквивалентный код с ветвями будет выглядеть так:

       mov.s32 var, 0;       // Executed by all threads.
       setp pred, condition; // Executed by all threads, sets predicate.

@!pred bra IF_FALSE;         // Conditional branches are predicated instructions.
IF_TRUE:                    // Label for clarity, not actually used.
       mov.s32 var, 1;
       bra IF_END;
IF_FALSE:
       mov.s32 var, 2;
IF_END:
       mov.s32 output, var;

Обратите внимание на то, что дольше это (5 инструкций для if). Для условной ветки требуется отключение части основы, выполнение первого пути, затем возврат к точке, где деформация расходится и выполняется второй путь, пока оба не сходятся. Это занимает больше времени, требует дополнительной бухгалтерии, дополнительной загрузки кода (особенно в случае, когда есть много инструкций для выполнения) и, следовательно, больше запросов на память. Все, что делает ветвление медленнее, чем простое предикатирование.

И в самом деле, в случае этого очень простого условного присваивания, компилятор может сделать еще лучше, всего лишь 2 инструкции для if:

mov.s32 var, 0;       // Executed by all threads.
setp pred, condition; // Executed by all threads, sets predicate.
selp var, 1, 2, pred; // Sets var depending on predicate (true: 1, false: 2).