Будет ли компилятор C / C ++ оптимизировать этот оператор if? - PullRequest
3 голосов
/ 01 марта 2009

У меня есть такой код, и мне трудно его прочитать:

// code1
if( (expensiveOperation1() && otherOperation() && foo()) 
     || (expensiveOperation2() && bar() && baz()) {
  // do something
}

Я просто изменил его на следующее, чтобы сделать его более читабельным:

// code2
const bool expr1 = expensiveOperation1() && otherOperation() && foo();
const bool expr2 = expensiveOperation2() && bar() && baz();
if(expr1 || expr2){
   // one of the conditions met
}

Но должен ли я сейчас беспокоиться об эффективности?

Я имею в виду, что в code1, если первое конъюнктивное предложение выполнено, тогда даже не стоит смотреть на второе, потому что уже ясно, что утверждение будет верным.

Но в моем более читаемом примере должны быть вычислены как cond1, так и cond2. Или компилятор будет достаточно умен, чтобы изменить мой code2 на code1, если expr2 больше нигде не используется?

Ответы [ 7 ]

24 голосов
/ 02 марта 2009

Я бы сказал, что не следует, поскольку они не являются логически эквивалентными, если какая-либо из функций имеет побочные эффекты.

Однако следующее будет эквивалентно, и у него будет то преимущество, что вы сможете давать описательные имена тестовым функциям, делая код более самодокументированным:

// code3
inline bool combinedOp1()
{
    return expensiveOperation1() && otherOperation() && foo();
}

inline bool combinedOp2()
{
    return expensiveOperation2() && bar() && baz();
}

А затем назовите это следующим образом:

if (combinedOp1() || combinedOp2())
{
    // do something
}
20 голосов
/ 02 марта 2009

Может быть, но почему бы не сделать так, чтобы ваш второй чек включал первый?

// code3
bool expr = expensiveOperation1() && otherOperation() && foo();
expr = expr || (expensiveOperation2() && bar() && baz());
if(expr){
   // one of the conditions met
}

Еще лучше, переверните вещи так, чтобы сначала в каждом списке была самая дешевая проверка, используя ленивую оценку, чтобы полностью пропустить дорогостоящие операции.

4 голосов
/ 02 марта 2009

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

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

2 голосов
/ 10 марта 2009

Лучшие ответы здесь отвечают на вопрос «не должен» и «возможно»! Это не окончательный ответ, давай!

Если вы хотите знать, оптимизирует ли ваш компилятор этот крошечный кусочек кода, скомпилируйте ваш код с флагом «показывать вывод сборки». На GCC этот флаг "-S". Затем посмотрите на выходную сборку, и она покажет вам ТОЧНО 100%, что компилируется или нет.

Затем вы можете сравнить свой первый фрагмент кода с фрагментом кода из «отсюда» и быстро попробовать многочисленные изменения кода, пока не найдете тот, который компилятор оптимизирует лучше всего (т.е. наименьшее количество циклов).

Звучит сложно и страшно смотреть на вывод asm, но на самом деле это занимает всего около 5 минут. Я сделал пример здесь: Какой самый быстрый способ поменять значения в C?

1 голос
/ 02 марта 2009

Ответ на этот вопрос, конечно, зависит от компилятора. Определенный способ проверить это посмотреть на сборку, сгенерированную компилятором для этой функции. У большинства (всех?) Компиляторов есть способ сделать это, например, gcc имеет опцию -S. Если по какой-то странной причине у вас нет, большинство отладчиков могут показать вам разборку для функции, или есть другие инструменты для этого.

0 голосов
/ 27 июля 2009

Компилятор может оптимизировать, если он знает, что функции в cond2 (дорогойOperation2 (), bar () и baz ()) являются чистыми (т.е. не имеют побочных эффектов). Если они чистые, самый простой способ убедиться, что компилятор знает, что это сделать их встроенными функциями.

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

FWIW, если эти функции чистые, вам, вероятно, следует переупорядочить их, чтобы bar () и baz () выполнялись перед дорогойOperation2 () (и то же самое для упорядочения в cond1).

0 голосов
/ 10 марта 2009

Хорошие ответы.

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

Я просто хочу, чтобы компилятор делал то, что ему говорят.

Если он способен перехитрить меня, он может перехитрить и себя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...