Насколько большой стек для хранения рекурсивных функций. Какие факторы, такие как ОС, компилятор и оборудование, следует учитывать - PullRequest
0 голосов
/ 02 октября 2019

Привет! Я выполнял рекурсивный код, и я понял, что на моем персональном компьютере, при запуске отладчика Visual Studio по умолчанию, я могу уместить 6776 раз только при использовании параметра «Выполнить без отладки» до того, как получу ошибку переполнения стека. При использовании отладчика я смог получить его до 6781. На cpp.sh мне удалось получить слишком 900 миллионов. Я подозреваю, что кеширование, но оно все еще не объясняет все. Поскольку он более точен, чем onlinegdb, со значительным запасом, который составляет только 205000. Мой компьютер значительно менее способен, чем сервер cpp.sh, но не в 132,723 раза меньше. Какие программные и аппаратные хитрости используются и как сделать их совместимыми?

Я использую Windows 10 версии 1903, используя Intel N3350 с 4 ГБ оперативной памяти. enter image description here

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);
}

кэшированная запись 900 000 000

кэшированная запись 100 000 000

код вычитания и выполнение

[кэшированное 100000000 доcrash] [6]

кэшировал 900000000 после аварии кэшировал 100000000 после аварии

кэшировал 60-значный код вычитания и выполнял

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

Ответы [ 2 ]

1 голос
/ 02 октября 2019

Почему результаты кардинально отличаются? Один компилятор оптимизирует рекурсию до цикла. Другой компилятор или тот же, что и при других настройках оптимизации, этого не делает. Это все, что нужно.

Насколько большой стек? Каждая реализация обычно документирует это и предоставляет некоторые средства для изменения значения. По умолчанию для настольных операционных систем обычно составляет около 1 МБ.

1 голос
/ 02 октября 2019

В Windows типичный размер стека потока 1Mb (может отличаться для разных наборов инструментов - GCC, VC ++ и т. Д., Но в таком порядке). Чтобы изменить размер стека, обратитесь к документации вашей инструментальной цепочки.

Каждый вызов будет использовать один кадр стека, размер которого будет определяться размером его локальных переменных и параметров, а также размером адреса возврата и учета. для дополнения выравнивания данных.

Если бы ваш стек был 1 Мб, а вы достигли глубины 6776, то это могло бы предложить, возможно, 154 байта на вызов, что кажется довольно высоким - я не могу объяснить это так много из представленного кода. Отладочная сборка может дополнить кадр стека для поддержки определения переполнения. Несмотря на это, я бы оценил, возможно, 60 байт, но все еще значимо. Тогда на глубине 900 миллионов может потребоваться стек в 50 Гб, что кажется неправдоподобным.

В отладчике вы можете наблюдать регистр SP, чтобы увидеть, насколько он меняется при вводе Solver(). Однако, выполняя отлаживаемый код, вы, вероятно, также отключите оптимизацию, которая может стать реальным ответом на вопрос, почему вы получаете более высокую производительность в онлайновых системах.

...