Introduction
This article was originally posted at blogs.microsoft.co.il/blogs/Eyal.
Recursive function – is a function that is partially defined by itself and consists of some simple case with a known answer. Example: Fibonacci number sequence, factorial function, quick sort and more.
Some of the algorithms/functions can be represented in an iterative way and some may not.
Iterative functions – are loop based imperative repetitions of a process (in contrast to recursion which has a more declarative approach).
Comparison between Iterative and Recursive Approaches from Performance Considerations
Factorial
static int FactorialRecursive(int n)
{
if (n <= 1) return 1;
return n * FactorialRecursive(n - 1);
}
static int FactorialIterative(int n)
{
int sum = 1;
if (n <= 1) return sum;
while (n > 1)
{
sum *= n;
n--;
}
return sum;
}
| N |
Recursive |
Iterative |
| 10 |
334 ticks |
11 ticks |
| 100 |
846 ticks |
23 ticks |
| 1000 |
3368 ticks |
110 ticks |
| 10000 |
9990 ticks |
975 ticks |
| 100000 |
stack overflow |
9767 ticks |
As we can clearly see, the recursive is a lot slower than the iterative (considerably) and limiting (stackoverflow).
The reason for the poor performance is heavy push-pop of the registers in the ill level of each recursive call.
Fibonacci
static int FibonacciIterative(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
int prevPrev = 0;
int prev = 1;
int result = 0;
for (int i = 2; i <= n; i++)
{
result = prev + prevPrev;
prevPrev = prev;
prev = result;
}
return result;
}
static int FibonacciRecursive(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
return FibonacciRecursive(n - 1) + FibonacciRecursive(n - 2);
}
static Dictionary<int> resultHistory = new Dictionary<int>();
static int FibonacciRecursiveOpt(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
if (resultHistory.ContainsKey(n))
return resultHistory[n];
int result = FibonacciRecursiveOpt(n - 1) + FibonacciRecursiveOpt(n - 2);
resultHistory[n] = result;
return result;
}
| N |
Recursive |
Recursive opt. |
Iterative |
| 5 |
5 ticks |
22 ticks |
9 ticks |
| 10 |
36 ticks |
49 ticks |
10 ticks |
| 20 |
2315 ticks |
61 ticks |
10 ticks |
| 30 |
180254 ticks |
65 ticks |
10 ticks |
| 100 |
too long/stack overflow |
158 ticks |
11 ticks |
| 1000 |
too long/stack overflow |
1470 ticks |
27 ticks |
| 10000 |
too long/stack overflow |
13873 ticks |
190 ticks |
| 100000 |
too long/stack overflow |
too long/stack overflow |
3952 ticks |
As before, the recursive approach is worse than iterative however, we could apply memorization pattern (saving previous results in dictionary for quick key based access), although this pattern isn't a match for the iterative approach (but definitely an improvement over the simple recursion).
Notes
- The calculations may be wrong in big numbers, however the algorithms should be correct.
- For timer calculations, I used
System.Diagnostics.Stopwatch.
Points of Interest
- Try not to use recursion in system critical locations.
- Elegant solutions not always the best performing when used in "recursive situations".
- If you required to use recursion, at least try to optimize it with dynamic programming approaches (such as memorization).
History