Sometimes I find myself writing tail recursive functions. I have been searching high and low, and I have found there is tail recursion in the .NET framework, but I'm not sure in what cases I can, and in what cases I can't use tail recursion efficiently. For example, I have a simple tree, lets call it
public class Tree
{
public Tree parent = null;
public Tree(Tree parent)
{
this.parent = parent;
this.children = new List<Tree>();
}
public List<Tree> children {get; private set;}
public Tree root
{
get
{
return this.parent == null ? this : parent.root;
}
}
}
for the root property, will the compiler emit a loop? will it emit .tail? Will the jitter respect the .tail? will nothing fancy happen, and will the algorithm just run recursively? Most importantly, should I rewrite this to be iterative?
In case of recursion, every call to itself is pushed to the call stack till we reach the base condition. So, we find the recursive implementation slower and heavier as compared to a similar implementation using looping. On the other hand, an iterative function doesn't have the overhead of repeated function calls.
In tail recursion, there is no other operation to perform after executing the recursive function itself; the function can directly return the result of the recursive call. In simple words, in tail recursion, the recursive function is called last. So it is more efficient than non-tail recursion.
One of the big differences between recursion and looping is the way that a recursive function terminates. In the above example, a for loop ends at the end of the sequence it is looping over. However, a recursive function could continue indefinitely since it doesn't necessarily have a sequence of data.
The tail recursion is better than non-tail recursion. As there is no task left after the recursive call, it will be easier for the compiler to optimize the code. When one function is called, its address is stored inside the stack. So if it is tail recursion, then storing addresses into stack is not needed.
The C# compiler will never emit tail
prefix.
F# will do so, if the call is in tail position. It depends on the order you traverse the tree whether tail recursion is applicable.
In your code, there is nothing in a tail position. The reason is the usage of a ternary operator. If the code is rewritten to use if
statements with each branch returning, then the call to parent.root
will be in tail position.
In terms of optimization, the compiler (F# or IronScheme) will normally convert tail recursive calls into while (true) { ... }
loops. This is done as it removes both the tail call and the need to call the method again.
So if C# was allowed to emit tail calls (which it is not) it would likely be transformed from:
public Tree root
{
get
{
if (parent == null) return this;
else return parent.root; // now in tail position
}
}
to (just a guestimate)
public Tree root
{
get
{
Tree temp = this;
while (true)
{
if (temp.parent == null)
{
return temp;
}
temp = temp.parent;
}
}
}
F# and IronScheme both does the same transformation. This is called tail call elimination
(TCE). And yes, it will be marginally faster than the C# version. (I have tested this by microbenchmarking fib
on C#, F# and IronScheme)
There is similar answer other answer.
Regarding speed. Tail recursion optimization isn't really different from loop for small functions. When tail call optimization fired it just replace "call" instruction (on x86) with "jmp". When doing the same through loop you'll have the same "jmp" instruction for entering next cycle. One moment you should remember is that the whole body of function will be the body of cycle and thus you should try to minimize size of recursive functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With