Why should recursion be preferred over iteration?

PerformanceLanguage AgnosticRecursionIteration

Performance Problem Overview


Iteration is more performant than recursion, right? Then why do some people opine that recursion is better (more elegant, in their words) than iteration? I really don't see why some languages like Haskell do not allow iteration and encourage recursion? Isn't that absurd to encourage something that has bad performance (and that too when more performant option i.e. recursion is available) ? Please shed some light on this. Thanks.

Performance Solutions


Solution 1 - Performance

> Iteration is more performant than > recursion, right?

Not necessarily. This conception comes from many C-like languages, where calling a function, recursive or not, had a large overhead and created a new stackframe for every call.

For many languages this is not the case, and recursion is equally or more performant than an iterative version. These days, even some C compilers rewrite some recursive constructs to an iterative version, or reuse the stack frame for a tail recursive call.

Solution 2 - Performance

Try implementing depth-first search recursively and iteratively and tell me which one gave you an easier time of it. Or merge sort. For a lot of problems it comes down to explicitly maintaining your own stack vs. leaving your data on the function stack.

I can't speak to Haskell as I've never used it, but this is to address the more general part of the question posed in your title.

Solution 3 - Performance

Haskell do not allow iteration because iteration involves mutable state (the index).

Solution 4 - Performance

As others have stated, there's nothing intrinsically less performant about recursion. There are some languages where it will be slower, but it's not a universal rule.

That being said, to me recursion is a tool, to be used when it makes sense. There are some algorithms that are better represented as recursion (just as some are better via iteration).

Case in point:

fib 0 = 0
fib 1 = 1
fib n = fib(n-1) + fib(n-2)

I can't imagine an iterative solution that could possibly make the intent clearer than that.

Solution 5 - Performance

Here is some information on the pros & cons of recursion & iteration in c:

http://www.stanford.edu/~blp/writings/clc/recursion-vs-iteration.html

Mostly for me Recursion is sometimes easier to understand than iteration.

Solution 6 - Performance

Several things:

  1. Iteration is not necessarily faster
  2. Root of all evil: encouraging something just because it might be moderately faster is premature; there are other considerations.
  3. Recursion often much more succinctly and clearly communicates your intent
  4. By eschewing mutable state generally, functional programming languages are easier to reason about and debug, and recursion is an example of this.
  5. Recursion takes more memory than iteration.

Solution 7 - Performance

Iteration is just a special form of recursion.

Solution 8 - Performance

Recursion is one of those things that seem elegant or efficient in theory but in practice is generally less efficient (unless the compiler or dynamic recompiler) is changing what the code does. In general anything that causes unnecessary subroutine calls is going to be slower, especially when more than 1 argument is being pushed/popped. Anything you can do to remove processor cycles i.e. instructions the processor has to chew on is fair game. Compilers can do a pretty good job of this these days in general but it's always good to know how to write efficient code by hand.

Solution 9 - Performance

I don't think there's anything intrinsically less performant about recursion - at least in the abstract. Recursion is a special form of iteration. If a language is designed to support recursion well, it's possible it could perform just as well as iteration.

In general, recursion makes one be explicit about the state you're bringing forward in the next iteration (it's the parameters). This can make it easier for language processors to parallelize execution. At least that's a direction that language designers are trying to exploit.

Solution 10 - Performance

In Java, recursive solutions generally outperform non-recursive ones. In C it tends to be the other way around. I think this holds in general for adaptively compiled languages vs. ahead-of-time compiled languages.

Edit: By "generally" I mean something like a 60/40 split. It is very dependent on how efficiently the language handles method calls. I think JIT compilation favors recursion because it can choose how to handle inlining and use runtime data in optimization. It's very dependent on the algorithm and compiler in question though. Java in particular continues to get smarter about handling recursion.

Quantitative study results with Java (PDF link). Note that these are mostly sorting algorithms, and are using an older Java Virtual Machine (1.5.x if I read right). They sometimes get a 2:1 or 4:1 performance improvement by using the recursive implementation, and rarely is recursion significantly slower. In my personal experience, the difference isn't often that pronounced, but a 50% improvement is common when I use recursion sensibly.

Solution 11 - Performance

As a low level ITERATION deals with the CX registry to count loops, and of course data registries. RECURSION not only deals with that it also adds references to the stack pointer to keep references of the previous calls and then how to go back.-

My University teacher told me that whatever you do with recursion can be done with Iterations and viceversa, however sometimes is simpler to do it by recursion than Iteration (more elegant) but at a performance level is better to use Iterations.-

Solution 12 - Performance

I find it hard to reason that one is better than the other all the time.

Im working on a mobile app that needs to do background work on user file system. One of the background threads needs to sweep the whole file system from time to time, to maintain updated data to the user. So in fear of Stack Overflow , i had written an iterative algorithm. Today i wrote a recursive one, for the same job. To my surprise, the iterative algorithm is faster: recursive -> 37s, iterative -> 34s (working over the exact same file structure).

Recursive:

private long recursive(File rootFile, long counter) {
			long duration = 0;
			sendScanUpdateSignal(rootFile.getAbsolutePath());
			if(rootFile.isDirectory()) {
				File[] files = getChildren(rootFile, MUSIC_FILE_FILTER);
				for(int i = 0; i < files.length; i++) {
					duration += recursive(files[i], counter);
				}
				if(duration != 0) {
					dhm.put(rootFile.getAbsolutePath(), duration);
					updateDurationInUI(rootFile.getAbsolutePath(), duration);
				}	
			}
			else if(!rootFile.isDirectory() && checkExtension(rootFile.getAbsolutePath())) {
				duration = getDuration(rootFile);
				dhm.put(rootFile.getAbsolutePath(), getDuration(rootFile));
				updateDurationInUI(rootFile.getAbsolutePath(), duration);
			}  
			return counter + duration;
		}

Iterative: - iterative depth-first search , with recursive backtracking

private void traversal(File file) {
			int pointer = 0;
			File[] files;
			boolean hadMusic = false;
			long parentTimeCounter = 0;
			while(file != null) {
				sendScanUpdateSignal(file.getAbsolutePath());
				try {
					Thread.sleep(Constants.THREADS_SLEEP_CONSTANTS.TRAVERSAL);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				files = getChildren(file, MUSIC_FILE_FILTER);
				
				if(!file.isDirectory() && checkExtension(file.getAbsolutePath())) {
					hadMusic = true;
					long duration = getDuration(file);
					parentTimeCounter = parentTimeCounter + duration;
					dhm.put(file.getAbsolutePath(), duration);
					updateDurationInUI(file.getAbsolutePath(), duration);
				}
				
				if(files != null && pointer < files.length) {
					file = getChildren(file,MUSIC_FILE_FILTER)[pointer];
				}
				else if(files != null && pointer+1 < files.length) {
					file = files[pointer+1];
					pointer++;
				}
				else {
					pointer=0;
					file = getNextSybling(file, hadMusic, parentTimeCounter);
					hadMusic = false;
					parentTimeCounter = 0;
				}
			}
		}

private File getNextSybling(File file, boolean hadMusic, long timeCounter) {
			File result= null;
			//se o file é /mnt, para
			if(file.getAbsolutePath().compareTo(userSDBasePointer.getParentFile().getAbsolutePath()) == 0) {
				return result;
			}
			File parent = file.getParentFile();
			long parentDuration = 0;
			if(hadMusic) { 
				if(dhm.containsKey(parent.getAbsolutePath())) {
					long savedValue = dhm.get(parent.getAbsolutePath());
					parentDuration = savedValue + timeCounter;
				}
				else {
					parentDuration = timeCounter; 
				}
				dhm.put(parent.getAbsolutePath(), parentDuration);
				updateDurationInUI(parent.getAbsolutePath(), parentDuration);
			}
			
			//procura irmao seguinte
			File[] syblings = getChildren(parent,MUSIC_FILE_FILTER);
			for(int i = 0; i < syblings.length; i++) {
				if(syblings[i].getAbsolutePath().compareTo(file.getAbsolutePath())==0) {
					if(i+1 < syblings.length) {
						result = syblings[i+1];
					}
					break;
				}
			}
			//backtracking - adiciona pai, se tiver filhos musica
			if(result == null) {
				result = getNextSybling(parent, hadMusic, parentDuration);
			}
			return result;
		}

Sure the iterative isn't elegant, but alhtough its currently implemented on an ineficient way, it is still faster than the recursive one. And i have better control over it, as i dont want it running at full speed, and will let the garbage collector do its work more frequently.

Anyway, i wont take for granted that one method is better than the other, and will review other algorithms that are currently recursive. But at least from the 2 algorithms above, the iterative one will be the one in the final product.

Solution 13 - Performance

I think it would help to get some understanding of what performance is really about. This link shows how a perfectly reasonably-coded app actually has a lot of room for optimization - namely a factor of 43! None of this had anything to do with iteration vs. recursion.

When an app has been tuned that far, it gets to the point where the cycles saved by iteration as against recursion might actually make a difference.

Solution 14 - Performance

Recursion is the typical implementation of iteration. It's just a lower level of abstraction (at least in Python):

class iterator(object):
    def __init__(self, max):
        self.count = 0
        self.max = max
    
    def __iter__(self):
        return self
    
    # I believe this changes to __next__ in Python 3000
    def next(self):
        if self.count == self.max:
            raise StopIteration
        else:
            self.count += 1
            return self.count - 1

# At this level, iteration is the name of the game, but
# in the implementation, recursion is clearly what's happening.
for i in iterator(50):
    print(i)

Solution 15 - Performance

I would compare recursion with an explosive: you can reach big result in no time. But if you use it without cautions the result could be disastrous.

I was impressed very much by proving of complexity for the recursion that calculates Fibonacci numbers here. Recursion in that case has complexity O((3/2)^n) while iteration just O(n). Calculation of n=46 with recursion written on c# takes half minute! Wow...

IMHO recursion should be used only if nature of entities suited for recursion well (trees, syntax parsing, ...) and never because of aesthetic. Performance and resources consumption of any "divine" recursive code need to be scrutinized.

Solution 16 - Performance

>Iteration is more performant than recursion, right?

Yes.

However, when you have a problem which maps perfectly to a Recursive Data Structure, the better solution is always recursive.

If you pretend to solve the problem with iterations you'll end up reinventing the stack and creating a messier and ugly code, compared to the elegant recursive version of the code.

That said, Iteration will always be faster than Recursion. (in a Von Neumann Architecture), so if you use recursion always, even where a loop will suffice, you'll pay a performance penalty.

https://stackoverflow.com/questions/2651112/is-recursion-ever-faster-than-looping/28033212#28033212

Solution 17 - Performance

"Iteration is more performant than recursion" is really language- and/or compiler-specific. The case that comes to mind is when the compiler does loop-unrolling. If you've implemented a recursive solution in this case, it's going to be quite a bit slower.

This is where it pays to be a scientist (testing hypotheses) and to know your tools...

Solution 18 - Performance

on ntfs UNC max path as is 32K
C:\A\B\X\C.... more than 16K folders can be created...

But you can not even count the number of folders with any recursive method, sooner or later all will give stack overflow.

Only a Good lightweight iterative code should be used to scan folders professionally.

Believe or not, most top antivirus cannot scan maximum depth of UNC folders.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDebashish12View Question on Stackoverflow
Solution 1 - PerformancenosView Answer on Stackoverflow
Solution 2 - PerformancedanbenView Answer on Stackoverflow
Solution 3 - PerformancekennytmView Answer on Stackoverflow
Solution 4 - PerformanceRHSeegerView Answer on Stackoverflow
Solution 5 - PerformanceJohn BokerView Answer on Stackoverflow
Solution 6 - Performanceuser24359View Answer on Stackoverflow
Solution 7 - PerformanceDon StewartView Answer on Stackoverflow
Solution 8 - PerformancehjorganView Answer on Stackoverflow
Solution 9 - PerformanceMichael BurrView Answer on Stackoverflow
Solution 10 - PerformanceBobMcGeeView Answer on Stackoverflow
Solution 11 - PerformanceMRFerociusView Answer on Stackoverflow
Solution 12 - PerformancejfvView Answer on Stackoverflow
Solution 13 - PerformanceMike DunlaveyView Answer on Stackoverflow
Solution 14 - PerformanceorokusakiView Answer on Stackoverflow
Solution 15 - Performanceivan_dView Answer on Stackoverflow
Solution 16 - PerformanceLucio M. TatoView Answer on Stackoverflow
Solution 17 - PerformanceAustin SalonenView Answer on Stackoverflow
Solution 18 - Performanceiam meView Answer on Stackoverflow