存储递归函数的堆栈有多大。我应该考虑哪些因素,如操作系统、编译器和硬件

Posted

技术标签:

【中文标题】存储递归函数的堆栈有多大。我应该考虑哪些因素,如操作系统、编译器和硬件【英文标题】:How big is the stack to store recursive functions. What factors like OS, compiler, and hardware should I consider 【发布时间】:2019-10-01 22:21:26 【问题描述】:

您好,我正在通过递归代码运行并且我意识到在我的个人计算机上运行默认的 Visual Studio 调试器时,在出现堆栈溢出错误之前,在使用不带调试选项的运行时,我只能运行 6776 次。使用调试器时,我可以将其调到 6781。在 cpp.sh 上,我可以得到 9 亿。我怀疑缓存,但它仍然不能解释一切。因为它比 onlinegdb 更准确,仅达到 205000。我的计算机的能力明显低于 cpp.sh 使用的服务器,但不是 132,723 倍。使用了哪些软件和硬件技巧以及如何使其保持一致?

我正在使用配备 4GB 内存的 Intel N3350 运行 Windows 10 版本 1903。

int main()


    long double Answer = 0;
    Answer = 3 + (Solver(2, 1, UserInput, 0));

double Solver(int counter, int group, int stop, long double partialanswer)

    long double Counter = counter;
    if (Counter >= stop)
    
        return partialanswer;
    
    if (group % 2 != 0)
    
        partialanswer = partialanswer + (4 / ((Counter) * (Counter + 1) * (Counter + 2)));
    
    else
    
        partialanswer = partialanswer - (4 / ((Counter) * (Counter + 1) * (Counter + 2)));
    
    Solver((counter + 2), (group+1), stop, partialanswer);

注意 3 如果需要,这里是完整的代码。

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
double Solver(int counter, int group, int stop, long double partialanswer)

    long double Counter = counter;
    if (Counter >= stop)
    
        return partialanswer;
    
    //partialanswer = round(partialanswer * pow(2, 54)) / pow(2, 54);
    if (group % 2 != 0)
    
        partialanswer = partialanswer + (4 / ((Counter) * (Counter + 1) * (Counter + 2)));
    
    else
    
        partialanswer = partialanswer - (4 / ((Counter) * (Counter + 1) * (Counter + 2)));
    
    Solver((counter + 2), (group+1), stop, partialanswer);


int main()


    int UserInput;
    long double Answer = 0;
    cin >> UserInput;
    Answer = 3 + (Solver(2, 1, UserInput, 0));
    cout << "PI = " << setprecision(18) << Answer << endl;
    cout << "reference Pi is equal to 3.1415926535897932384626433832795028841971 " << endl;
    cout << " The Difference is equal to " <<fixed << setprecision(30) << abs(Answer - 3.1415926535897932384626433832795028841971) << endl;

减法代码

#include <iostream>
#include <iomanip>
using namespace std;


int main()

    long double UserInput1;
    long double UserInput2;
    cin >> UserInput1;
    cin >> UserInput2;
    cout << fixed << setprecision(32) << (UserInput1 - UserInput2);

cached 900,000,000 entry

cached 100,000,000 entry

subtraction code and run

[崩溃前缓存100000000][6]

cached 900000000 after crash cached 100000000 after crash

cached Subtraction 60 digit code and run

----https://i.stack.imgur.com/j0QN2.png

【问题讨论】:

栈和堆相互向着增长。你拥有的对象越少,堆栈的增长空间就越大 Does C++ limit recursion depth?的可能重复 Windows 操作系统通常是 1MB 堆栈。您可以使用正确的构建选项来更改它,但它们取决于您的工具链,因为您同时标记了 GCC 和 Visual Studio,所以这很模糊。您正在使用带有 GCC 的 Visual Studio? 请注意,编译器可能很容易将您的 Solver 函数优化为循环,而不是使用堆栈进行递归调用。 您的代码有未定义的行为:函数Solver 应该返回一个值,但它在递归时不会返回。因此,您使用此特定代码进行的所有测试都没有实际意义。任何事情都有可能发生。 【参考方案1】:

在 Windows 中,典型的 thread stack size 1Mb (可能在工具链之间有所不同 - GCC、VC++ 等,但数量级不同)。要更改堆栈大小,请参阅您的工具链文档。

每次调用将消耗一个堆栈帧,其大小将由其局部变量和参数的大小,加上返回地址的大小和数据对齐填充的大小决定。

如果您的堆栈是 1Mb 并且您达到了 6776 的深度,那么这意味着每次调用可能需要 154 个字节,这似乎有点高 - 我无法从所提供的代码中解释那么多。调试版本可能会填充堆栈帧以支持溢出检测。即便如此,我估计可能有 60 个字节,所以仍然很重要。 9 亿的深度可能需要一个 50Gb 的堆栈,这似乎是不合理的。

在调试器中,您可以观察 SP 寄存器,以了解当您输入 Solver() 时它发生了多少变化。但是,通过运行可调试代码,您可能还会禁用优化,这可能是您在在线系统上获得更高性能的真正答案。

【讨论】:

【参考方案2】:

为什么结果大不相同?一种编译器将递归优化为循环。另一个编译器,或另一个优化设置的相同编译器,没有。仅此而已。

堆栈有多大?每个实现通常都会记录它,并提供一些更改值的方法。桌面操作系统的默认值通常约为 1 MiB。

【讨论】:

我不知道我是否应该删除这个(问题),但它是缓存数据。 @SS 我不知道你所说的“缓存数据”是什么意思。 我已将问题标记为删除,但输入 1 亿与 9 亿几乎相同,而 cpp.sh 只是没有完成它的工作。几乎没有用的信息 @SS 我不知道你最后的评论是什么意思。程序是否输出正确的结果? 不,不是在while循环中这样做会导致不同的结果

以上是关于存储递归函数的堆栈有多大。我应该考虑哪些因素,如操作系统、编译器和硬件的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 服务器的 thread_stack 参数 - 它是啥?它应该有多大?

在数据库存储结构设计时要考虑哪些因素

我需要将邮政编码存储在数据库中。柱子应该有多大?

Python中的递归有多安全?

某个程序的堆栈内存有多大,是不是有任何编译器标志可以设置它?

单个 RDD 记录可以有多大?