У меня есть «MyFunction», над которой я продолжаю зацикливаться, если я должен или не должен использовать goto, и в подобных (надеюсь, редких) обстоятельствах. Поэтому я пытаюсь выработать сложную привычку для этой ситуации. Делать или не делать.
int MyFunction()
{ if (likely_condition)
{
condition_met:
// ...
return result;
}
else /*unlikely failure*/
{ // meet condition
goto condition_met;
}
}
Я намеревался получить выгоду от неудачной инструкции условного перехода для вероятного случая. Однако я не вижу, как компилятор мог бы знать, что оптимизировать для вероятности случая без чего-либо подобного.
Современный ЦП будет использовать эту ветку в любом случае с одинаковой производительностью, если он сделает правильный прогноз ветвления. Так что, если это находится во внутреннем цикле, производительность if (unlikely) { meet condition } common code;
будет соответствовать тому, что вы написали.
Кроме того, если вы укажете общий код в обеих ветках, компилятор генерировать код, идентичный тому, что вы написали: общий случай будет передан для if
пункт и else
пункт будет jmp
к общему коду. Вы видите это все время с более простыми терминалами, такими как *out = whatever; return result;
, При отладке бывает сложно сказать, какие return
вы смотрите, потому что они все были объединены.
Мне кажется, что оптимизация, которую вы пытаетесь сделать, в основном устарела. Большинство современных процессоров имеют встроенный прогноз ветвления, поэтому (при условии, что он используется достаточно, чтобы заметить), они отслеживают, как часто берется ветвь, или нет, и предсказывают, вероятнее всего, ветвь будет взята, или нет, основываясь на прошлом шаблоне взятия или нет. , В этом случае скорость зависит, прежде всего, от того, насколько точен этот прогноз, а не от того, является ли прогноз принятым или не принятым.
Таким образом, вам, вероятно, лучше всего с более простым кодом:
int MyFunction() {
if (!likely_condition) {
meet_condition();
}
// ...
return result;
}
Похоже, код должен работать так, как вы ожидаете, пока condition_met:
не пропускает инициализации переменных.
Нет, и вы даже не знаете, что запутанная версия компилируется в более оптимальный код. Оптимизация компилятора (и прогнозирование ветвей процессора) в последнее время становятся очень умными.
3.
int MyFunction()
{
if (!likely_condition)
{
// meet condition
}
condition_met:
// ...
return result;
}
или, если это поможет вашему компилятору (проверьте сборку)
int MyFunction()
{
if (likely_condition); else
{
// meet condition
}
condition_met:
// ...
return result;
}
Я очень рекомендую использовать __builtin_expect()
макрос (GCC) или аналогичный для вашего конкретного компилятора C ++ (см. Подсказки по прогнозированию переносимых веток) Вместо того, чтобы использовать goto
:
int MyFunction()
{ if (__builtin_expect(likely_condition))
{
// ...
return result;
}
else /*unlikely failure*/
{ // meet condition
}
}
Как и другие упоминали goto
подвержен ошибкам и злу от костей.