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

时间:2012-11-15 16:05:13

标签: c++ gcc c++11 metaprogramming constexpr

以下程序将调用 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快速分配3 gb左右后开始交换。我还没有尝试使用clang 3.1,因为我现在无法访问它。

我唯一的猜测是,这与GCC试图过于聪明并记住函数调用有关,就像它对模板一样。如果是这样的话,他们没有限制他们做多少memoization,如MRU缓存表的大小或其他东西,这似乎并不奇怪吗?我还没有找到一个禁用它的开关。

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

编辑: 现在我已经在clang 3.1中尝试了它,它根本不会泄漏内存,无论我多长时间工作(即我制作MAXD有多高)。 CPU使用率几乎为100%,内存使用率几乎为0%,就像预期的那样。也许这只是GCC中的一个错误。

2 个答案:

答案 0 :(得分:2)

这可能不是关于constexpr的最终文件,但它是与gcc constexpr wiki.

相关联的主要文档

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

......它说......

  

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

所以,我希望你能够克服语言边界和gcc选择实现constexpr的方式(可能尝试生成内联的整个函数,然后评估/执行它)

答案 1 :(得分:1)

你的答案在你的评论中“通过运行函数运行时并观察它,虽然我可以让它运行很长时间”,这是由你内心最递归的有趣调用(x + 1,深度+ 1)引起的

通过删除constexpr将其更改为运行时函数而不是编译时函数,并观察到它运行了很长时间,这表明它正在非常深地递归。

当编译器执行该函数时,它必须深度递归,但不会使用堆栈进行递归,因为它实际上并不是在生成和执行机器代码。