views:

148

answers:

5

Possible Duplicate:
Is recursion ever faster than looping?

I was first trained to program seriously in C, about 15 years ago. My employer wanted highly optimized code for computationally difficult tasks. I remember being advised more than once to rewrite recursions as loops, even at the expensive of readability, in order to avoid "recursion overhead." As I understood it then, recursion overhead was the extra effort required to push data onto a stack and later pop it off.

Now I code in C, Python, Perl, and sometimes Java, and I wonder sometimes about recursions. Is there still something to be gained by rewriting them? What if they're tail recursions? Have modern compilers made all these issues moot? Are such concerns irrelevant for interpreted languages?

+2  A: 

I don't think any of the languages you mentioned require that the platform/compiler implements tail call elimination. You can find languages that do require this optimization - most functional languages have this requirement.

However another thing you need to consider is that computers have become orders of magnitudes faster than they were 15 years ago, so it's much rarer now then before that you need to worry about micro-optimizations. A program that 15 years ago might have required careful hand optimization in assembler to get decent performance might run blazingly fast on a modern computer, even if written in a higher level language like Java. That's not to say that performance is never a problem any more - but you should concentrate on choosing the correct algorithm and on writing readable code. Only make micro-optimizations after you have measured the performance and you can see that the code in question is the bottleneck.

One thing you do need to worry about though is overflowing the stack. If there's any risk of that happening it might be worth rewriting a recursive function in an iterative way instead.

Mark Byers
gcc does tail recursive optimization. (see e.g http://stackoverflow.com/questions/490324/how-do-i-check-if-gcc-is-performing-tail-recursion-optimization)
nos
@nos: Reworded - hopefully more correct now.
Mark Byers
+1  A: 

It is serious. Most of the languages I code in have a real cost to function calls (the compilers for them can generally do tail recursion as well so sometimes it's not an issue).

That cost, and the fact that the stack is not an unlimited resource, usually makes me tend to use recursion only for cases where I know there's a limit on the depth it can go to.

For example, I know a balanced binary tree search will only go fifty levels deep for one quadrillion entries. I wouldn't, however, use:

def sum1through (n):
    if n == 0 return 0
    return n + sum1through (n-1)

since doing that for n of twenty million would not be healthy for a stack.

paxdiablo
+2  A: 

The issue still exists. Recursion takes a lot of stack space, as each time a method calls itself, a pointer to it and its local variables are generated again. The number of function calls made during recursion makes an O(n) memory usage; compared to O(1) of a non-recursive function like loops.

Catie
... unless you're using tail recursion and a language/compiler/platform that understands in.
Freed
@Freed, yes but tail recursion is kind-of-another-way of writing a loop in my humble opinion.
Amigable Clark Kant
This also depends on the actual memory usage and the recursion depth. For known, bounded recursion depth this is usually a non-issue since in that case stack space costs nothing.
Konrad Rudolph
@Amigable - their equivalent, sure, but that doesn't mean the compiler is clever enough to treat them as such.
Freed
@Konrad, the stack space may not cost anything in the sense that it's already allocated, but will still require more physical memory. Also, pushing parameters on the stack, saving registers and whatever else you need to do is not for free.
Freed
@Freed: But since that amount of memory is bounded **and isn’t needed elsewhere** this is irrelevant. For all intents and purposes, bounded stack space takes away *no* memory. And since Catie’s posting was only about memory, not performance, this is what my “costs nothing” referred to. But apart from that, call stack operations are very inexpensive compared to a manual stack implementation.
Konrad Rudolph
@Konrad, memory is a shared resource, any time you actually reference memory (which is very different from "having an available memory adress" for it), you compete with other code for being in RAM (as opposed to swapped out to disk or uninitialized), and more importantly, being in the cache. The stack is surely more efficient than any manual implementation of a stack, but no stack usage at all is even better, which you can get if you implement tail recursion properly.
Freed
@Freed: I actually considered writing “costs almost nothing” with a reference to cache misses in my original comment but decided against it to keep it concise. Good to see that we’re on the same page here, though.
Konrad Rudolph
+6  A: 

Recursion can lead to significant overhead if the kernel of the recursive function is less computationally expensive than the function entry/exit code and the cost of the call itself. The best way to find out is simply to profile two versions of the code - one recursive, and one not.

That said, if your idea of avoiding recursion is to make a stack-like structure yourself, watch out - it may not necessarily be any faster than the more straightforward recursive approach. Again, profiling is your friend.

Finally, remember that programmer time is more expensive than CPU time. Before you micro-optimize your code, it really is a good idea to measure to see if it really will be an issue.

bdonlan
Note that compilers now can sometimes translate recursion into a simple iterative loop, even if the function is not tail-recursive. See http://www.linux-kongress.org/2009/slides/compiler_survey_felix_von_leitner.pdf.
liori
I’d be extremely surprised if someone pulled off writing a more efficient stack data structure than the CPU’s own call stack …! Of course, there may be other reasons to use a manual stack rather than rely on the call stack, but performance should never be one of them.
Konrad Rudolph
@Konrad, it's been done, see eg glibc's implementation of qsort (http://goo.gl/6ptK) - that said, it's not _easy_ to beat the compiler, and a naive attempt is likely to do more harm than good.
bdonlan
A: 

People say lots of silly things about performance.

  1. If you need recursion, like to do depth-first tree walk, then you need it, so use it.

  2. Before worrying about the performance of anything, find out if you have a problem and where it is.
    Performance problems are like con men and tricksters - they specialize in being where you least expect, so if you're worried about something specific like recursion, you are almost guaranteed to be worrying about the wrong thing.

In my opinion, the best way to find performance problems is by stack sampling on wall-clock time, and examining the samples to see what the program is doing, not just by getting measurements and wondering what they mean.

That said, if you do find 10% or more of time going into a recursive call, and nothing much else happens inside the recursive routine, and you can loop it, then looping it will probably help.

Mike Dunlavey