C++ 一个常量表达式怎么能计算得这么快

C++ 一个常量表达式怎么能计算得这么快,c++,constants,constexpr,C++,Constants,Constexpr,我一直在尝试编译时计算的const表达式。 但我使用了一个在编译时执行时速度非常快的示例 #include<iostream> constexpr long int fib(int n) { return (n <= 1)? n : fib(n-1) + fib(n-2); } int main () { long int res = fib(45); std::cout << res; return 0; }

我一直在尝试编译时计算的const表达式。 但我使用了一个在编译时执行时速度非常快的示例

#include<iostream> 

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

int main () {  
    long int res = fib(45); 
    std::cout << res; 
    return 0; 
} 
#包括
constexpr long int fib(int n){

返回(n编译器缓存较小的值,不需要像运行时版本那样重新计算。
(Optimizer非常优秀,可以生成大量代码,包括我无法理解的特殊情况下的欺骗;天真的2^45递归需要几个小时。)

如果还存储以前的值:

int cache[100] = {1, 1};

long int fib(int n) {
    int res = cache[n];
    return res ? res : (cache[n] = fib(n-1) + fib(n-2));
} 

运行时版本比编译器快得多。

您可能会发现5.4版的功能并没有完全消除,至少需要6.1版

我不认为有任何缓存发生。我相信优化器足够聪明,可以证明
fib(n-2)
fib(n-1)
之间的关系,并完全避免第二次调用。这是GCC 5.4输出(从godbolt获得),没有
constexpr
和-O2:

fib(long):
        cmp     rdi, 1
        push    r12
        mov     r12, rdi
        push    rbp
        push    rbx
        jle     .L4
        mov     rbx, rdi
        xor     ebp, ebp
.L3:
        lea     rdi, [rbx-1]
        sub     rbx, 2
        call    fib(long)
        add     rbp, rax
        cmp     rbx, 1
        jg      .L3
        and     r12d, 1
.L2:
        lea     rax, [r12+rbp]
        pop     rbx
        pop     rbp
        pop     r12
        ret
.L4:
        xor     ebp, ebp
        jmp     .L2

我必须承认我不理解-O3的输出-生成的代码异常复杂,有大量内存访问和指针算术,很可能有一些缓存(记忆化)完成这些设置。

我猜想编译器会缓存对
fib
的函数调用。上面的fibonacci数的实现速度很慢。尝试在运行时代码中缓存函数值,速度会快得多。这种递归fibonacci非常低效(它有指数运行时),所以我猜编译时求值比这更聪明,并优化了计算。@AlanBirtles是的,我用-O3编译了它。我假设编译器缓存函数调用该函数只需执行46次(每个可能的参数0-45执行一次)而不是2^45次。但是我不知道gcc是否能这样工作。@我知道的某个程序员。但是,当计算在运行时花费这么多时间时,编译怎么能这么快?除非你做一些缓存,否则无法避免递归两次。你认为优化器实现了一些缓存吗?你能在编译器中显示这一点吗输出,因为这真的很有趣?…也可能是编译器而不是缓存编译器能够证明fib(n-2)和fib(n-1)之间的某种关系,而不是调用fib(n-1),它使用fib(n-2)值进行计算。我认为这与我在5.4版的输出中看到的匹配。删除constexpr并使用-O2。是否有链接或其他源代码解释在编译时可以进行哪些优化?只要可观察的行为保持不变,优化器几乎可以自由做任何事情。给定
fib
函数没有任何副作用影响(无参考外部变量,输出仅取决于输入),有了一个聪明的优化器,可以做很多事情。@Suma只递归一次是没有问题的。因为有一个迭代版本,当然也有一个递归版本,例如使用尾部递归。我想我错了。在.L3处有一个循环,fib在所有较低的fib上循环。对于-O2,它仍然是指数型的。