Подтвердить что ты не робот

Constexpr вопрос, почему эти две разные программы работают в таком разное время с g++?

Я использую 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-битной платформе.

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

const unsigned long joe = fib(92);

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