【发布时间】:2015-04-14 14:32:36
【问题描述】:
假设我有一些操作图形结构的递归函数:
typedef struct Node {
Data data;
size_t visited;
size_t num_neighbors;
struct Node *neighbors[];
} Node;
void doSomethingWithNode(Node *node)
{
if ((!node) || node->visited)
return;
node->visited = 1;
/* Do something with node->data */
size_t /* some local variables */;
size_t i;
for (i = 0; i < node->num_neighbors; i++)
{
/* Do some calculations with the local variables */
if (/* something */)
doSomethingWithNode(node->neighbors[i]);
}
}
由于我在循环中使用了局部变量,编译器 (gcc) 为这个函数创建了一个比我想的更大的堆栈帧(大量的 pushq 和 popq 指令即使使用-O3),这是一个问题,因为它是深度递归的。由于访问节点的顺序无关紧要,我可以重构此代码以使用Node 指针堆栈,从而将开销减少到每次迭代一个指针。
- 是否有任何提示可以给编译器 (
gcc) 以解决此问题? - 如果没有,是否可以在不借助汇编的情况下将调用堆栈本身用于我的指针堆栈?
【问题讨论】:
-
所有递归代码也可以使用循环表示为非递归。如果默认的 8MB(在 Linux 上)不够,您还可以在链接时增加堆栈大小(使用例如
-z stack-sizelinker option)。虽然我并不真正认为有必要,因为局部变量的数量相对较小(当然取决于“一些局部变量”)并且没有数组。并且局部变量并没有真正用push和pop指令处理,所以你真的在看正确的代码吗? -
在 gcc 手册页中简短查看后,我看到了一个选项 -fconserve-stack。你试过了吗?
-
@Marian:谢谢!我试试看。
-
@Marian 我刚刚尝试用
-fconserve-stack编译一个编程语言实现。它对探测最大递归深度的测试程序没有任何影响:无论有没有使用该选项编译的解释器,都可以实现相同数量的递归调用。time make tests也没有区别。该选项有一个通用名称,但可能针对在它执行任何操作之前必须出现的特定情况。也许你必须在同一个函数中有很多不重叠的块作用域,它们可以折叠到同一个堆栈空间或其他任何地方。
标签: c gcc recursion stack callstack