views:

2515

answers:

9

Are there any general rules when using recursion on how to avoid stackoverflows?

+1  A: 

Other than having a reasonable stack size and making sure you divide and conquer your problem such that you continually work on a smaller problem, not really.

Jeff Moser
+7  A: 

I am not aware of any hard set to avoid stackoverflows. I personally try to ensure -
1. I have my base cases right.
2. The code reaches the base case at some point.

batbrat
Ok Ted. In recursive functions, there are two parts -1. The part that reduces the problem and calls itself --recursive case. 2.The part that represents the smallest instance of the problem, that has a (usually) trivial answer -base case.
batbrat
The recursive calling ends and begins to rewind when a base case is reached. I was trying to say that the base condition should be reached at some point.
batbrat
http://en.wikipedia.org/wiki/Recursion_(computer_science) is a good place to look.
batbrat
For example factorial(n) = n * (n-1) * ... * 1. Here, the base case is 1 since factorial(1) = 1. Code: factorial(n): if n ==1: return 1 else return factorial(n-1) Note -This is a bad example of using recursion -- tail recursion. Still, it gets the point accross.
batbrat
So, unless n=1 is reached by the above code, it will recurse infinitely and cause a stack overflow
batbrat
+3  A: 

If you're finding yourself generating that many stack frames, you might want to consider unrolling your recursion into a loop.

Especially if you are doing multiple levels of recursion (A->B->C->A->B...) you might find that you can extract one of those levels into a loop and save yourself some memory.

+1, good answer. I've seen tree parsing done in a loop. Was much quicker and stack safe.
Dead account
+21  A: 

How many times you will be able to recurse will depend on:

  • The stack size (which is usually 1MB IIRC, but the binary can be hand-edited; I wouldn't recommend doing so)
  • How much stack each level of the recursion uses (a method with 10 uncaptured Guid local variables will be take more stack than a method which doesn't have any local variables, for example)
  • The JIT you're using - sometimes the JIT will use tail recursion, other times it won't. The rules are complicated and I can't remember them. (There's a blog post by David Broman back from 2007, and an MSDN page from the same author/date, but they may be out of date by now.)

How to avoid stack overflows? Don't recurse too far :) If you can't be reasonably sure that your recursion will terminate without going very far (I'd be worried at "more than 10" although that's very safe) then rewrite it to avoid recursion.

Jon Skeet
I think that TCO only happens on 64-bit IIRC, for all other cases you need to still prefix the call with tail. And if you dont play with the rules, that might not even be tail called.
leppie
@leppie: Exactly the sort of rules I wasn't remembering :) I'll see if I can dig up the blog post on it.
Jon Skeet
@Jon here's one I found: http://blogs.msdn.com/davbr/pages/tail-call-jit-conditions.aspx
John Price
The 32-bit section mentions "tail. prefix does not exist in the IL stream" as a reason it won't optimize the tail call; I don't believe the C# compiler ever emits the tail IL (though other compilers like F# may).
John Price
@John Price: the JIT is free to do a tail call even if you do not specify so, as long as it is safe to do so. And thanks for that link (also saw it many moons ago). Oh and F# like Scheme is fully tail recursive.
leppie
If I Remember Correctly.
Jon Skeet
Added that page to the answer, thanks John.
Jon Skeet
@Jon, I agree with the last paragraph in your answer. If the logical limit of your recursion is not "knowable" then recursion is very likely the wrong route to take.
Michael Meadows
+1  A: 

The normal limit, if not much is left on the stack between successive calls, is around 15000-25000 levels deep. 25% of that if you are on IIS 6+.

Most recursive algorhitms can be expressed iteratively.

There are various way to increase allocated stack space, but I'll rather let you find an iterative version first. :)

leppie
+5  A: 

It really depends on what recursive algorithm you're using. If it's simple recursion, you can do something like this:

public int CalculateSomethingRecursively(int someNumber)
{
    return doSomethingRecursively(someNumber, 0);
}

private int doSomethingRecursively(int someNumber, int level)
{
    if (level >= MAX_LEVEL || !shouldKeepCalculating(someNumber))
        return someNumber;
    return doSomethingRecursively(someNumber, level + 1);
}

It's worth noting that this approach is really only useful where the level of recursion can be defined as a logical limit. In the case that this cannot occur (such as a divide and conquer algorithm), you will have to decide how you want to balance simplicity versus performance versus resource limitations. In these cases, you may have to switch between methods once you hit an arbritrary pre-defined limit. An effective means of doing this that I have used in the quicksort algorithm is to do it as a ratio of the total size of the list. In this case, the logical limit is a result of when conditions are no longer optimal.

Michael Meadows
+1 - was going to say the same
Dead account
Although I like this solution, wouldn't it be machine dependant? some systems would be able to handle more levels, and there's no way (no simple way) to determine it at run time. Also, if you're NEEDING that many stack frames, you would be crippling the program with this.
You're right, but I would argue that the limit should be logical (based on the algorithm), not physical (based on the machine capabilities). Otherwise, you're going to hit the wall if you ever parallelize.
Michael Meadows
Also, if it's not a logical limit, then it's really just arbitrary and could result in results of variable preciseness.
Michael Meadows
+1  A: 

I just thought of tail-recursion, but it turned out, that C# does not support it. However the .Net-Framework seems to support it:

http://blogs.msdn.com/abhinaba/archive/2007/07/27/tail-recursion-on-net.aspx

tanascius
Notice that the produced IL isn't everything that matters. The JIT *does* on occasions perform tail-call optimization on the IL code (see Jon's answer).
Konrad Rudolph
A: 

Remember, if you have to ask about system limits, then you are probably doing something horribly wrong.

So, if you think you might get a stack overflow in normal operation then you need to think of a different approach to the problem.

It's not difficult to convert a recursive function into an iterative one, especially as C# has the Generic::Stack collection. Using the Stack type moves the memory used into the program's heap instead of the stack. This gives you the full address range to store the recursive data. If that isn't enough, it's not too difficult to page the data to disk. But I'd seriously consider other solutions if you get to this stage.

Skizz

Skizz
I am not asking about system limits I just attempting to gain some knowledge....
Ted Smith
+1  A: 

The default stack size for a thread is 1 MB, if you're running under the default CLR. However, other hosts may change that. For example the ASP host changes the default to 256 KB. This means that you may have code that runs perfectly well under VS, but breaks when you deploy it to the real hosting environment.

Fortunately you can specify a stack size, when you create a new thread by using the correct constructor. In my experience it is rarely necessary, but I have seen one case where this was the solution.

You can edit the PE header of the binary itself to change the default size. This is useful if you want to change the size for the main thread. Otherwise I would recommend using the appropriate constructor when creating threads.

Brian Rasmussen