Results 1 to 9 of 9

Thread: Does VB.Net optimize for tail-end recursion?

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2006
    Posts
    675

    Does VB.Net optimize for tail-end recursion?

    Private Function Fac(ByVal Num As Decimal) As Decimal
    'Factorial
    If Num = 1 Then
    Return 1
    Else
    Return Num * Fac(Num - 1)
    End If
    End Function


    This function uses tail-end recusion, that is, the last thing it does is recur. It uses:
    Return Num * Fac(Num - 1)
    instead of
    Return Fac(Num - 1) * Num

    Because both would give you the same answer, but I know that some languages (Scheme, for instance) optimize tail-end recursion. So, if you can help it, recurring is the last thing you should do, and you code will fun faster.

    Does anyone know if VB does this?
    VB.Net 2008
    .Net Framework 2.0

    "Must you breathe? 'Cause I need heaven..."

  2. #2
    Frenzied Member circuits2's Avatar
    Join Date
    Sep 2006
    Location
    Kansas City, MO
    Posts
    1,027

    Re: Does VB.Net optimize for tail-end recursion?

    I don't think it matters in VB. I found this example on MSDN:

    Code:
    Function Factorial(ByVal N As Integer) As Integer
       If N <= 1 Then      ' Reached end of recursive calls.
          Return 1         ' N = 0 or 1, so climb back out of calls.
       Else                ' N > 1, so call Factorial again.
          Return Factorial(N - 1) * N
       End If
    End Function
    Show the love! Click (rate this post) under my name if I was helpful.

    My CodeBank Submissions: How to create a User Control | Move a form between Multiple Monitors (Screens) | Remove the MDI Client Border | Using Report Viewer with Visual Studio 2012 Express

  3. #3
    New Member
    Join Date
    Aug 2013
    Posts
    2

    Question Re: Does VB.Net optimize for tail-end recursion?

    Quote Originally Posted by 18experience View Post
    Private Function Fac(ByVal Num As Decimal) As Decimal
    'Factorial
    If Num = 1 Then
    Return 1
    Else
    Return Num * Fac(Num - 1)
    End If
    End Function

    This function uses tail-end recusion, that is, the last thing it does is recur. It uses:
    Return Num * Fac(Num - 1)
    instead of
    Return Fac(Num - 1) * Num
    I have trouble seeing the example as tail recursion. It looks like head recursion, to me. The reason it looks like head recursion is what happens when I rewrite it removing all function chaining. I might be using the term "function chaining" different from what is standard. What I mean by function chaining is the output from a function is used directly as the input to another function. Here is how I rewrote the Fac() without function chaning:

    Private Function Fac(ByVal Num As Decimal) As Decimal
    'Factorial
    If Num = 1 Then
    Return 1
    Else
    Dim Num2 As Decimal = Fac(Num - 1)
    Dim RetVal As Decimal = Num * Num2
    Return RetVal
    End If
    End Function
    You see that there are statements after the recursive call to Fac(). If there are any computations after a recursive call, I don't think it can be tail recursive. However, I would call it head recursive. Seems to me that head recursion tends to be more frugal on using the stack, and tends to be more compact in coding.

    Here is how I would write Fac() using tail recursion and without function chaining:

    Private Function Fac(ByVal Num As Decimal, ByVal CumulativeResult As Decimal) As Decimal
    'Factorial
    If Num = 1 Then
    Return CumulativeResult
    Else
    Dim Num2 As Decimal = Num * CumulativeResult
    Dim RetVal As Decimal = Fac(Num - 1, Num2)
    Return RetVal
    End If
    End Function
    In this rewritten version, there is no calculation after the recursive call. This is tail recursion. When you need the function's return value to do a calculation, you have to recurse the function first, therefore the calculation will be after, and it is not tail recursive.

    And if you like, here is the function rewritten as tail recursive with function chaining:

    Private Function Fac(ByVal Num As Decimal, ByVal CumulativeResult As Decimal) As Decimal
    'Factorial
    If Num = CumulativeResult Then
    Return 1
    Else
    Return Fac(Num - 1, Num * CumulativeResult)
    End If
    End Function
    If I am mistaken, I hope someone will post to explain my error.
    Last edited by indinfer; Aug 12th, 2013 at 08:47 PM.

  4. #4
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    Re: Does VB.Net optimize for tail-end recursion?

    Quote Originally Posted by indinfer View Post
    If I am mistaken, I hope someone will post to explain my error.
    I didn't read your explanation, but I agree it's not tail recursion, because no matter which side of the * operator you put the recursive call, the * operation comes last.

  5. #5
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: Does VB.Net optimize for tail-end recursion?

    I can confirm that the VB.Net compiler doesn't optimize for tail-end recursion. Take this function:-
    vbnet Code:
    1. '
    2.     Public Function FacTailRec(ByVal acc As Integer, ByVal n As Integer)
    3.         If n < 2 Then Return acc
    4.         Return FacTailRec(n * acc, n - 1)
    5.     End Function

    The VB.Net compiler produces this MSIL for the above function:-

    asm Code:
    1. ;
    2. .method public instance object  FacTailRec(int32 acc,
    3.                                            int32 n) cil managed
    4. {
    5.   // Code size       24 (0x18)
    6.   .maxstack  4
    7.   .locals init ([0] object FacTailRec)
    8.   IL_0000:  ldarg.2
    9.   IL_0001:  ldc.i4.2
    10.   IL_0002:  bge.s      IL_000b
    11.   IL_0004:  ldarg.1
    12.   IL_0005:  box        [mscorlib]System.Int32
    13.   IL_000a:  ret
    14.   IL_000b:  ldarg.0
    15.   IL_000c:  ldarg.2
    16.   IL_000d:  ldarg.1
    17.   IL_000e:  mul.ovf
    18.   IL_000f:  ldarg.2
    19.   IL_0010:  ldc.i4.1
    20.   IL_0011:  sub.ovf
    21.   IL_0012:  callvirt   instance object WindowsApplication1.Form1::FacTailRec(int32,
    22.                                                                              int32)
    23.   IL_0017:  ret
    24. } // end of method Form1::FacTailRec

    My understanding is that the optimization changes tail-end calls into loops in order reduce the overhead of function calls like stack space. Notice the above MSIL method is defined to call itself before returning which means tail-end recursion has not been optimized by the VB.Net compiler.

    Now this doesn't mean that the JIT doesn't. Its entirely possible that the JIT will make the optimization. Here is an MSDN article that talks about the tail call optimizations in the CLR.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  6. #6
    New Member
    Join Date
    Aug 2013
    Posts
    2

    Re: Does VB.Net optimize for tail-end recursion?

    Quote Originally Posted by Niya View Post
    I can confirm that the VB.Net compiler doesn't optimize for tail-end recursion. Take this function:-
    vbnet Code:
    1. '
    2.     Public Function FacTailRec(ByVal acc As Integer, ByVal n As Integer)
    3.         If n < 2 Then Return acc
    4.         Return FacTailRec(n * acc, n - 1)
    5.     End Function
    By the way, your example is correct. My example was wrong because I put "If n = 1 Then Return 1" I should have put "If n = 1 Then Return CumulativeResult". I should have tested my code even though I "know" what it does. So, I have gone back and corrected my post.

    Thank you for posting your example. And I believe that your example is indeed tail recursion.

    And I think that you have demonstrated that we cannot rely on VB to optimize tail recursion. I did not learn to read MSIL. But I do see this recursive call:
    IL_0012: callvirt instance object WindowsApplication1.Form1::FacTailRec(int32, int32)
    But we should not see a recursive call if it is optimized out! VB does not optimize out recursive calls.
    Last edited by indinfer; Aug 12th, 2013 at 08:51 PM.

  7. #7
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: Does VB.Net optimize for tail-end recursion?

    Quote Originally Posted by indinfer View Post
    I did not learn to read MSIL. But I do see this recursive call:
    IL_0012: callvirt instance object WindowsApplication1.Form1::FacTailRec(int32, int32)
    But we should not see a recursive call if it is optimized out! VB does not optimize out recursive calls.
    Exactly. That would have been optimized to a loop. In such a case you would have seen compare and jump instructions but instead it calls itself passing the arguments on the stack.

    Quote Originally Posted by indinfer View Post
    I suppose that the MSIL is from the regular VB compiler (whatever it is called) not from the Just-In-Time (JIT) compiler?
    Regardless, I would feel shaky relying on the optimization.
    The JIT is the component of the CLR that turns that MSIL code into machine code on the fly to be executed by the CPU. According to the link I posted, later versions of the CLR take tail-recursion optimization into account when compiling MSIL.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  8. #8
    Registered User
    Join Date
    Jul 2013
    Posts
    2

    Re: Does VB.Net optimize for tail-end recursion?

    No,both C# and VB.NET don't.F# does.

  9. #9
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: Does VB.Net optimize for tail-end recursion?

    The F# compiler has to, its a functional language where recursion would be as common as loops in imperative languages. But more importantly, the JIT according to the link I posted, didn't consider tail-recursive optimizations too seriously and there are too many conditions that prevent its use so I would guess the F# compiler guys would have to take matters into their own hands and do the optimization at the MSIL level so they wouldn't be at the mercy of the JIT. When dealing with a functional language, tail-recursive optimizations must be taken seriously.
    Last edited by Niya; Aug 13th, 2013 at 04:25 AM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width