编译时如何(指数)比运行时更快?

beh*_*uri 48 c++ compiler-construction gcc constexpr c++11

以下代码通过指数缓慢的算法计算斐波纳契数:

#include <cstdlib>
#include <iostream>

#define DEBUG(var) { std::cout << #var << ": " << (var) << std::endl; }

constexpr auto fib(const size_t n) -> long long
{
    return n < 2 ? 1: fib(n - 1) + fib(n - 2);
}

int main(int argc, char *argv[])
{
    const long long fib91 = fib(91);

    DEBUG( fib91 );
    DEBUG( fib(45) );

    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

我在运行时计算第45个Fibonacci数,在编译时计算第91个.

有趣的是,GCC 4.9编译代码并fib91在几分之一秒内完成计算,但需要一段时间才能吐出fib(45).

我的问题:如果GCC足够聪明以优化fib(91)计算而不是采用指数缓慢的路径,那么它会阻止它做同样的事情fib(45)吗?

以上是否意味着GCC产生两个编译版本的fib函数,其中一个是快速的而另一个是指数级的慢?

问题在于编译器如何优化fib(91)计算(是的!它确实使用了一种记忆),但如果它知道如何优化fib函数,为什么它不会这样做fib(45)呢?并且,是否有两个单独的fib函数编译?一个慢,另一个快?

Tim*_*lds 38

GCC可能是记忆constexpr功能(启用Θ(n)计算fib(n)).这对编译器来说是安全的,因为constexpr函数纯粹是功能性的.

比较Θ(N)"编译器算法"(使用记忆化)到您的Θ(φ ñ运行时间算法)(其中φ是黄金比例),突然它非常有意义,编译器是如此之快.

constexpr上cppreference页(强调):

constexpr说明符声明可以在编译时评估函数或变量的值.

constexpr说明符并没有宣布它需要评估在编译时函数或变量的值.因此,人们只能猜测GCC正在使用什么启发式来选择是在编译时还是在语言规则不需要编译时计算的运行时进行评估.它可以根据具体情况选择,但仍然是正确的.

如果你想强制编译器constexpr在编译时评估你的函数,这里有一个简单的技巧.

constexpr auto compute_fib(const size_t n) -> long long
{
    return n < 2 ? n : compute_fib(n - 1) + compute_fib(n - 2);
}

template <std::size_t N>
struct fib
{
    static_assert(N >= 0, "N must be nonnegative.");
    static const long long value = compute_fib(N);
};
Run Code Online (Sandbox Code Playgroud)

在其余代码中,您可以访问fib<45>::valuefib<91>::value保证在编译时对它们进行评估.

  • @ behzad.nouri应用于函数的`constexpr`的语义意味着函数返回值*可以*被视为编译时常量.它们不要求*它被视为编译时常量.我用额外的信息更新了答案. (2认同)

pdw*_*pdw 16

在编译时,编译器可以记住函数的结果.这是安全的,因为该函数是constexpr,因此将始终返回相同输入的相同结果.

在运行时它理论上可以做同样的事情.但是,大多数C++程序员都会对优化传递感到不满,导致隐藏的内存分配.

  • `constexpr`函数应该有一个`runtime_memoize`属性.在`fib`的情况下,你很清楚它只会被调用少量输入,因此缓存永远不会很大.但是除了害怕程序员皱眉之外,我希望编译器编写者害怕不知道缓存需要多大才能增长. (2认同)
  • @SteveJessop:问题是编译器不知道运行时输入是什么,所以它不能预先分配(足够)内存.它将需要在转换下具有类似`std :: vector <std :: tuple <params ... >>`的东西,然后在调整大小期间遇到异常安全问题....更好地通过让程序员写它.(现在如果所有输入都具有较小的已知范围,则_that_可能) (2认同)