Вопрос от constexpr, почему эти две разные программы работают с g ++ в такое разное время? - PullRequest
11 голосов
/ 15 августа 2011

Я использую gcc 4.6.1 и получаю интересное поведение, связанное с вызовом функции constexpr. Эта программа работает очень хорошо и сразу распечатывает 12200160415121876738.

#include <iostream>

extern const unsigned long joe;

constexpr unsigned long fib(unsigned long int x)
{
   return (x <= 1) ? 1 : (fib(x - 1) + fib(x - 2));
}

const unsigned long joe = fib(92);

int main()
{
   ::std::cout << "Here I am!\n";
   ::std::cout << joe << '\n';
   return 0;
}

Эта программа работает вечно, и у меня никогда не было терпения ждать, пока она напечатает значение:

#include <iostream>

constexpr unsigned long fib(unsigned long int x)
{
   return (x <= 1) ? 1 : (fib(x - 1) + fib(x - 2));
}

int main()
{
   ::std::cout << "Here I am!\n";
   ::std::cout << fib(92) << '\n';
   return 0;
}

Почему такая огромная разница? Я что-то не так делаю во второй программе?

Редактировать: Я компилирую это с g++ -std=c++0x -O3 на 64-битной платформе.

Ответы [ 4 ]

14 голосов
/ 15 августа 2011

joe - выражение интегральной константы;он должен использоваться в границах массива.По этой причине разумный компилятор будет оценивать его во время компиляции.

Во второй программе, даже если компилятор мог вычислить его во время компиляции, нет причин, по которым он должен это делать.

4 голосов
/ 15 августа 2011

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

Где, поскольку вторая версия фактически оценивается во время выполнения без поиска таблиц оцененных константных выражений, это означает, что вычисление fib (92) делает что-то вроде 2 ** 92 рекурсивных вызовов.

Другими словами, компилятор не оптимизирует тот факт, что fib (92) является константным выражением.

2 голосов
/ 09 марта 2012

Я также хотел посмотреть, как gcc оптимизировал код для этого нового ключевого слова constexpr, и на самом деле это просто потому, что вы вызываете fib (92) как параметр оператора ofstream :: operator << </p>

::std::cout << fib(92) << '\n';

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

const unsigned long joe = fib(92);

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

2 голосов
/ 15 августа 2011

Для компилятора есть место, чтобы решить не оценивать во время компиляции, если он считает, что что-то "слишком сложно".Это в тех случаях, когда не обязательно проводить оценку для генерации правильной программы, которая действительно может быть запущена (как указывает @MSalters).

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

Кажется, если вы хотите гарантию в коде, что он будет выполнять оптимизацию, тогда вы нашли способ для этого.Но если constexpr-depth не может помочь, я не уверен, есть ли какие-либо соответствующие флаги компилятора в противном случае ...

...