Оптимизация компилятором C ++ переданных аргументов - PullRequest
9 голосов
/ 12 ноября 2008

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

WARN(
     "Danger Will Robinson! There are "
     + boost::lexical_cast<string>(minutes)
     + " minutes of oxygen left!"
);

Я использую встроенную функцию для WARN, но мне интересно, сколько оптимизации происходит за кулисами - оценка аргументов во всей программе будет дорогостоящей. Функция WARN выглядит примерно так:

bool WARNINGS_ENABLED = false;
inline void WARN(const string &message) {
    if (!WARNINGS_ENABLED) {
       return;
    }
    // ...
}

Учитывая, что построение строкового аргумента не имеет побочных эффектов, оптимизирует ли его компилятор? Требуется ли определенный уровень оптимизации (-Ox в g++ для некоторых x)?

Ответы [ 5 ]

12 голосов
/ 12 ноября 2008

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

Вам нужно переименовать вашу функцию в WARN2 и добавить макрос вроде:

#define WARN(s) do {if (WARNINGS_ENABLED) WARN2(s);} while (false)

Это предотвратит оценку s во время выполнения, если у вас не включены предупреждения.

Функция do-while - это хитрость, позволяющая использовать его в любом месте кода (голый оператор, оператор в фигурном блоке if, оператор в свободном блоке if, фигурный оператор в скобках и т. Д. .

6 голосов
/ 12 ноября 2008

Вы можете проверить, что делает GCC / G ++, используя опцию -S . Это выведет код до его фактической сборки - см. gcc (1) .

GCC и G ++ более или менее ведут себя одинаково в этом случае. Поэтому я сначала перевел код на C, чтобы провести дальнейшие тесты:

char WARNINGS_ENABLED = 0;

inline void WARN(const char* message) {
    if (!WARNINGS_ENABLED) {
        return;
    }
    puts(message);
}

int main() {
    WARN("foo");
    return 0;
}

запустите gcc -O3 -S file.c и посмотрите в выходной файл ' file.s '
Вы увидите, что GCC ничего не удалил !

Это не то, что вы просили, но для того, чтобы дать компилятору возможность оптимизировать этот код, вам нужно сделать WARNINGS_ENABLED постоянной . Альтернативой является сделать его статическим и не изменять значение в этом файле. Но : делая его статическим имеет побочный эффект, что символ не экспортируется.

static const char WARNINGS_ENABLED = 0;

inline void WARN(const char* message) {
  if (!WARNINGS_ENABLED) {
      return;
  }
  puts(message);
}

int main() {
    WARN("foo");
    return 0;
}

GCC затем полностью очищает код.

1 голос
/ 12 ноября 2008

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

Кстати, если WARN - встроенная функция, вы все равно заплатите цену за построение сообщения (что очень неэффективно в вашем примере со строками lexical_cast и operator +), даже если оно отключено.

Вот некоторые эффективные (минимальные (близкие к нулю с ЦП прогнозирования ветвления) издержки при отключении времени выполнения) макросов ведения журналов , которые поддерживают как функции, так и стиль ведения журнала потоков.

1 голос
/ 12 ноября 2008

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

Я не эксперт по бусту, но я предполагаю, что есть способ построить лямбду, которая будет оцениваться только для генерации строки, если WARNINGS_ENABLED имеет значение true. Что-то вроде ...

inline void warnFunc(some_boost_lambda &message_generator) {
  if (WARNINGS_ENABLED) {
    cerr << message_generator() << endl;
  }
}

#define WARN(msg) warnFunc(...insert boost magic here to turn msg into a lambda...)
0 голосов
/ 12 ноября 2008

Разве вы не можете просто определить все это с помощью препроцессора?

void inline void LogWarning(const string &message) 
{
  //Warning
}

#ifdef WARNINGS_ENABLED
#define WARN(a) LogWarning(a)
#else
#define WARN(a)
#endif

Так работает макрос ASSERT (). Весь код внутри скобок в WARN даже не проходит через препроцессор к компилятору. Это означает, что вы можете делать другие вещи, как

#ifdef WARNINGS_ENABLED
// Extra setup for warning
#endif
//....
WARN(uses setup variables)

И это скомпилирует оба пути.

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

...