views:

1382

answers:

9

I know that recursion is sometimes a lot cleaner than looping, and I'm not asking anything about when I should use recursion over iteration, I know there are lots of questions about that already.

What I'm asking is, is recursion ever faster than a loop? To me it seems like, you would always be able to refine a loop and get it to perform more quickly than a recursive function because the loop is absent constantly setting up new stack frames.

I'm specifically looking for whether recursion is faster in applications where recursion is the right way to handle the data, such as in some sorting functions, in binary trees, etc.

A: 

In general, no, recursion will not be faster than a loop in any realistic usage that has viable implementations in both forms. I mean, sure, you could code up loops that take forever, but there would be better ways to implement the same loop that could outperform any implementation of the same problem via recursion.

You hit the nail on the head regarding the reason; creating and destroying stack frames is more expensive than a simple jump.

However, do note that I said "has viable implementations in both forms". For things like many sorting algorithms, there tends to not be a very viable way of implementing them that doesn't effectively set up its own version of a stack, due to the spawning of child "tasks" that are inherently part of the process. Thus, recursion may be just as fast as attempting to implement the algorithm via looping.

Edit: This answer is assuming non-functional languages, where most basic data types are mutable. It does not apply to functional languages.

Amber
That's also why several cases of recursion are often optimized by compilers in languages where recursion is frequently used. In F#, for instance, in addition to full support to tail recursive functions with the .tail opcode, you often see a recursive function compiled as a loop.
emaster70
Yep. Tail recursion can sometimes be the best of both worlds - the functionally "appropriate" way to implement a recursive task, and the performance of using a loop.
Amber
This is not, in general, correct. In some environments, mutation (which interacts with GC) is more expensive than tail recursion, which is transformed into a simpler loop in the output which does not use an extra stack frame.
Dietrich Epp
+3  A: 

Tail recursion is as fast as looping. Many functional languages have tail recursion implemented in them.

mkorpela
Tail recursion *can be* as fast as looping, when a tail call optimization is implemented: http://c2.com/cgi/wiki?TailCallOptimization
Joachim Sauer
A: 

In any realistic system, no, creating a stack frame will always be more expensive than an INC and a JMP. That's why really good compilers automatically transform tail recursion into a call to the same frame, i.e. without the overhead, so you get the more readable source version and the more efficient compiled version. A really, really good compiler should even be able to transform normal recursion into tail recursion where that is possible.

Kilian Foth
+41  A: 

This depends on the language being used. You wrote 'language-agnostic', so I'll give some examples.

In Java, C, and Python, recursion is fairly expensive compared to iteration (in general) because it requires the allocation of a new stack frame. In some C compilers, one can use a compiler flag to eliminate this overhead, which transforms certain types of recursion (actually, certain types of tail calls) into jumps instead of function calls.

In functional programming language implementations, sometimes, iteration can be very expensive and recursion can be very cheap. In many, recursion is transformed into a simple jump, but changing the loop variable (which is mutable) sometimes requires some relatively heavy operations, especially on implementations which support multiple threads of execution. Mutation is expensive in some of these environments because of the interaction between the mutator and the garbage collector, if both might be running at the same time.

I know that in some Scheme implementations, recursion will generally be faster than looping.

In short, the answer depends on the code and the implementation. Use whatever style you prefer. If you're using a functional language, recursion might be faster. If you're using an imperative language, iteration is probably faster. In some environments, both methods will result in the same assembly being generated (put that in your pipe and smoke it).

Addendum: In some environments, the best alternative is neither recursion nor iteration but instead higher order functions. These include "map", "filter", and "reduce" (which is also called "fold"). Not only are these the preferred style, not only are they often cleaner, but in some environments these functions are the first (or only) to get a boost from automatic parallelization — so they can be significantly faster than either iteration or recursion. Data Parallel Haskell is an example of such an environment.

List comprehensions are another alternative, but these are usually just syntactic sugar for iteration, recursion, or higher order functions.

Dietrich Epp
I +1 that, and would like to comment that "recursion" and "loops" are just what humans name their code. What matters for performance is not how you *name* things, but rather how they are compiled/interpreted. Recursion, by definition, is a mathematical concept, and has little to do with stack frames and assembly stuff.
Pavel Shved
Also, recursion is, in general, the more natural approach in functional languages, and iteration is normally more intuitive in imperative languages. The performance difference is unlikely to be noticeable, so just use whatever feels more natural for that particular language. For example, you probably wouldn't want to use iteration in Haskell when recursion is much more simple.
musicfreak
A: 

This is a guess. Generally recursion probably doesn't beat looping often or ever on problems of decent size if both are using really good algorithms(not counting implementation difficulty) , it may be different if used with a language w/ tail call recursion(and a tail recursive algorithm and with loops also as part of the language)-which would probably have very similar and possibly even prefer recursion some of the time.

Roman A. Taycher
A: 
TheMachineCharmer
Iterative "counterpart" could be significantly faster if implemented in resemblance to recursive one (-1).
Pavel Shved
@Pavel Shved: How can it be done?
TheMachineCharmer
@Charmer, are you aware of a data structure called "Stack"? Push into it the answers "odd/even" when dividing N towards one, and then pop them, accumulating result. (That's a generic way to convert recursion into iteration.) Furthermore, you can do it without stack by just bit-shifting N.
Pavel Shved
Yes.I am aware of the data structure called stack :) Would you please elaborate your instructions? Thanks!
TheMachineCharmer
@Charmer, come on, I'm sure you can do it yourself. Here's a question that might be of help: http://stackoverflow.com/questions/1549943/design-patterns-for-converting-recursive-algorithms-to-iterative-ones
Pavel Shved
+4  A: 

Consider what absolutely must be done for each, iteration and recursion.

  • iteration: a jump to beginning of loop
  • recursion: a jump to beginning of called function

You see that there is not much room for differences here.

(I assume recursion being a tail-call and compiler being aware of that optimization).

Pasi Savolainen
A: 

According to theory its the same things. Recursion and loop with the same O() complexity will work with the same theoretical speed, but of course real speed depends on language, compiler and processor. Example with power of number can be coded in iteration way with O(ln(n)):

  int power(int t, int k) {
  int res = 1;
  while (k) {
    if (k & 1) res *= t;
    t *= t;
    k >>= 1;
  }
  return res;
  }
Ydabkaa
+1  A: 

Recursion may well be faster where the alternative is to explicitly manage a stack, like in the sorting or binary tree algorithms you mention.

I've had a case where rewriting a recursive algorithm in Java made it slower.

So the right approach is to first write it in the most natural way, only optimize if profiling shows it is critical, and then measure the supposed improvement.

starblue