Explore c # tail recursive Compiler optimization,
Reading directory:
Recursive Application
A function calls itself directly or indirectly. This function can be called a recursive function.
static int RecFact(int x){ if (x == 0) return 1; return x * RecFact(x - 1);}RecFact(10);
The above is the implementation of a classic factorial function. Here are two steps:
- Convert the factorial of 10 to 10 *9 !, 10(9*8 !).... The scale of each conversion is smaller.
- Approximation, 0 when converting to the minimum scale !, Solution 1. Start reverse merge and gradually approach to 10 to get the solution.
Here, x = 0 is our boundary condition (that is, the termination condition), and some depend on external variables as the boundary.
If a recursive function has no boundaries, it will not be able to stop (infinite loop to memory overflow). Of course, this is meaningless.
RecFact call Stack:
Common Use Cases:
- Factorial/Fibonacci series/Tower
- Traverse Hard Disk Files
- InnerExceptions exception catch (exception. InnerException = null)
Tail recursion Optimization
When the boundary is unclear, recursion is prone to overflow.
During the factorial process, the stack needs to save the return address of each call (RecFact) and the status of all local variables at that time. During this period, the stack space cannot be released (that is, overflow is prone ).
In order to optimize the occupation of the stack, a method for optimizing tail recursion is proposed.
static void Recurse(int x) { Console.WriteLine(x); if (x == 10) return; Recurse(x + 1); } Recurse(0);
The difference between Recursion and factorial is that there is no return value. That is to say, the stack does not need to save the last returned address/status value. This is the idea of tail recursion optimization.
Tail recursion can solve the overflow problem caused by recursion too deep, because the stack can be released/reused during recursion.
Compiler Optimization
Tail recursion optimization looks pretty good, but it is a bit messy in net.
- Net is optimized when JIT is compiled into an assembly in C.
- Net on IL, there is a special command tail to implement tail recursion optimization (F ).
We execute Recurse (0) (x = 1000000) to draw the following conclusion:
C #/64-bit/Release has the JIT compiler for tail recursion optimization (non-C # Compiler Optimization ).
JIT in C #/32-bit or C #/Debug mode is not optimized.
F # optimize tail recursion in two cases:
1,A simple tail recursion is optimized to A while LOOP, as shown below:
let rec Recurse(x) = if (x = 1000) then true else Recurse(x + 1)
(Convenient demonstration of C # rendering) optimized:
public static bool Recurse(int x) { while (x != 0x3e8) { x++; } return true; }
2,Complex Tail recursion: The F # compiler will generate the IL command Tail for optimization, as shown below:
let Recurse2 a cont = cont (a + 1)
Optimized:
How to define complex tail recursion? It is usually the next transmission mode (CPS ).
F # In debug mode, you need to configure it during compilation:
Summary
In C # Language (procedural/object-oriented programming ideas), loops are preferred, rather than recursion or tail recursion. But in functional programming, recursive/tail recursion is the mainstream usage, just like using loops in C.
References
Http://volgarev.me/blog/62412678347
Http://stackoverflow.com/questions/15864670/generate-tail-call-opcode