How to allow more memory and avoid stack overflow on lots of recursion?

前端 未结 5 2008
-上瘾入骨i
-上瘾入骨i 2021-01-06 19:33

I\'m testing the timing of an algorithm that does lots of recursive calls. My program dies at about 128k recursive calls, and this takes only .05 seconds. I\'d like to allow

5条回答
  •  囚心锁ツ
    2021-01-06 19:58

    Although other answers talk about how to either avoid recursion altogether, or how to use tail recursion, or how to simply set a larger stack size, I think for completeness that it's worthwhile to consider memory usage patterns (to answer "how to allow more memory ... on lots of recursion").

    Out of habit, many programmers will allocate buffers inside the recursive function, and reallocate new buffers when the function is called recursively:

    int recursive_function(int x)
    {
        if (1 == x)
            return 1;
        int scratchpad[100];
        ... // use scratchpad
        return recursive_function(x-1) + scratchpad[x-1];
    }
    

    Since this is a throwaway sample, I won't bother worrying about invalid input (negative values, values larger than 100) and I will assume somebody asking a question about programming either knows how to do that or is smart enough to find out.

    The important point here is that scratchpad takes up 400 bytes (on a 32 bit machine, 800 bytes on a 64 bit machine) of the stack each and every time recursive_function() is called, so if recursive_function() is called recursively 100 times, then 40,000 bytes (or 80,000 bytes on a 64 bit machine) of stack space are being used for buffers, and it's very likely you can modify the function to reuse the same buffer on each call:

    int recursive_function(int x, int* buffer, int buffer_length)
    {
        if (1 == x)
            return 1;
        ... // use buffer (and buffer_length to avoid overflow)
        int temp_value = buffer[x-1];
        return recursive_function(x-1, buffer, buffer_length) + temp_value;
    }
    

    Of course you could instead use a std::vector, which handles some details for you to protect you against memory leaks and buffer overruns (and, for the record, keeps the data on the heap [see footnote], meaning it will likely use less stack space).

    40k or even 80k may not seem like much but things can add up. If the function doesn't have many other stack-allocated variables then this can dominate stack space (that is, if it weren't for the extra space the buffers take up you may be able to call the function far more times).

    This may seem obvious, but it does come up, even in nonrecursive functions. Additionally, buffers aren't always obvious as arrays. They may be strings or objects, for instance.


    Footnote: STL containers, such as arrays don't necessarily put all their data on the heap. They actually take a template argument to specify the memory allocation used; it's just that the allocator they use by default puts the data on the heap. Obviously unless you specify an allocator that somehow puts the data on the stack, the end result will be the same: using STL containers will probably use less memory than using stack allocated arrays or objects.

    I say "probably" because although the data is kept on the heap (or somewhere else), the container can only access that data through pointers it keeps internally, if the container is on the stack then those pointers will reside on the stack, and those pointers take up space. So a one or two element std::vector may actually take up more space on the stack than the corresponding array.

提交回复
热议问题