Gcc Вероятно, маловероятное использование макросов

Я пишу критический кусок кода с примерно следующей логикой

if(expression is true){
   //do something with extremely low latency before the nuke blows up. This branch is entered rarely, but it is the most important case
}else{
   //do unimportant thing that doesnt really matter
}

Я думаю использовать макрос likely() вокруг выражения, поэтому, когда он попадает в важную ветвь, я получаю минимальную задержку.

Мой вопрос заключается в том, что использование действительно противоположно предложенному названию макроса, потому что я выбираю маловероятную ветвь для предварительной выборки, т.е. важная ветвь вряд ли произойдет, но это самая важная вещь, когда это происходит.

Есть ли явный недостаток в этом с точки зрения производительности?

Ответ 1

Да. Вы обманываете компилятор, помечая маловероятную, но обязательную ветвь, как если бы она была вероятной ветвью, в надежде, что компилятор сделает ее быстрее.

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

Существует также гораздо менее вероятный, но все же возможный недостаток, что некоторая версия некоторого компилятора, который вы используете сейчас или в будущем, будет делать разные вещи, чем вы ожидаете с вероятным макросом, и эти разные вещи не будут что вы хотели обмануть компилятор, и в итоге вы получите код, который каждый раз через цикл тратит $100 тыс. спекулятивно, получая 90% от завершения работы реактора, прежде чем уничтожить его.

Ответ 2

Это абсолютно противоположно традиционному использованию __builtin_expect (x, 1), который используется в смысле макроса:

#define likely(x) __builtin_expect(x, 1)

который я лично считаю плохим (так как вы загадочно отмечаете маловероятный путь как вероятность увеличения производительности). Тем не менее, вы все равно можете отметить эту оптимизацию, поскольку __builtin_expect (x) не делает никаких предположений о ваших потребностях, заявляя о пути "likey" - это просто стандартное использование. Чтобы делать то, что вы хотите, я бы предложил:

#define optimize_path(x) __builtin_expect(x, 1)

который будет делать то же самое, но вместо того, чтобы код обвинил маловероятный путь, вероятно, вы теперь делаете код, описывая, что вы действительно пытаетесь - оптимизировать критический путь.

Однако я должен сказать, что если вы планируете синхронизировать ядерное оружие - вы должны не только проверять вручную (и время) скомпилированную сборку, чтобы время было правильным, но вы также должны использовать RTOS. Неправильное предсказание ветки будет иметь чрезвычайно незначительный эффект, до такой степени, что здесь почти нет необходимости, поскольку вы можете компенсировать событие "1 в миллион", просто имея более быстрый процессор или правильно синхронизируя задержку для ошибочного предсказания. Что влияет на современные компьютерные тайминги - это приостановка и планирование ОС. Если вам нужно что-то происходить в очень дискретном масштабе времени, вы должны планировать их в режиме реального времени, а не в psuedo-real time, что и в большинстве операционных систем общего назначения. Неправильное предсказание в отрасли, как правило, в сотни раз меньше задержки, которая может возникнуть из-за отсутствия использования ОСРВ в ситуации RT. Обычно, если вы считаете, что неверное предсказание ветки может быть проблемой, вы удаляете ветвь из чувствительной к времени проблемы, так как предиктор отрасли обычно имеет состояние, которое является сложным и не контролируется. Макрос "вероятный" и "маловероятный" относится к блокам кода, которые могут быть удалены из разных областей, с различными состояниями прогнозирования ветвлений и, что самое важное, используются очень часто. Высокая частота ударов по этим ветвям приводит к ощутимому увеличению производительности для приложений, которые его используют (например, ядра Linux). Если вы только нажмете ветку один раз, вы можете получить повышение производительности на 1 наносекунду в некоторых случаях, но если приложение всегда критично, есть другие вещи, которые вы можете сделать, чтобы помочь себе значительно увеличить производительность.