为什么这个constexpr代码会导致GCC吃掉我所有的RAM?

以下程序将调用fun 2 ^(MAXD + 1)次。 尽管(如果我的想法是正确的话),最大递归深度不应该超过MAXD。 因此编译可能需要一些时间,但它不应该吃我的RAM。

#include<iostream>

const int MAXD = 20;

constexpr int fun(int x, int depth=0){
  return depth == MAXD ? x : fun(fun(x + 1, depth + 1) + 1, depth + 1);
}

int main(){
  constexpr int i = fun(1);
  std::cout << i << std::endl;
}

问题是,吃我的RAM正是它所做的。 当MAXD高达30时,我的笔记本电脑在GCC 4.7.2快速分配3GB左右后开始交换。 我还没有尝试过与叮当3.1,因为我现在无法访问它。

我唯一的猜测是,这与GCC试图过于聪明并且记忆函数调用有关,就像模板一样。 如果是这样的话,他们没有限制他们做多少记忆,例如MRU高速缓存表的大小或什么的没有限制吗? 我还没有找到一个开关来禁用它。

我为什么要这样做? 我正在制作一个高级编译时间库的想法,比如遗传编程或其他东西。 由于编译器没有编译时间尾部调用优化,所以我担心任何循环都需要递归,并且(即使我调出最大递归深度参数,这似乎有点难以要求)会快速分配所有的RAM并填充它与毫无意义的堆栈帧。 因此,我提出了上述解决方案,可以在没有深度堆栈的情况下获取任意多个函数调用。 这种功能可以用于折叠/循环或蹦床。

编辑:现在我已经尝试在叮当3.1,它不会泄漏内存,不管我使它工作多长时间(即我做MAXD多高)。 与预期的一样,CPU使用率几乎为100%,内存使用率几乎为0%。 也许这只是GCC中的一个bug。


这可能不是关于constexpr的权威文档,但它是来自gcc constexpr wiki的主要文档。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf

...它说...

我们(仍然)在常量表达式中禁止所有形式的递归。 这不是绝对必要的,因为在常量表达式评估中对递归深度的实现限制会使我们远离编译器永远递归的可能性。 但是,直到我们看到一个令人信服的递归用例,我们不打算允许它。

所以,我希望你碰到语言边界和gcc选择实现constexpr的方式(可能试图在线生成整个函数,然后评估/执行它)


你的回答是在你的评论中“通过运行函数运行时并且观察到,尽管我可以使其运行很长时间”,这是由你最内在递归调用fun(x + 1,depth + 1)引起的。

当您通过删除constexpr将其更改为运行时函数而非编译时函数时,发现它运行了很长时间,这表明它正在递归得非常深。

当函数由编译器执行时,它必须深入递归,但不会将堆栈用于递归,因为它实际上并不生成和执行机器代码。

链接地址: http://www.djcxy.com/p/11419.html

上一篇: Why does this constexpr code cause GCC to eat all my RAM?

下一篇: Globally logging jQuery Errors (Event & DOM errors)