Tail Functions and Unrolling #3640
Replies: 21 comments
-
This feels like bike-shedding for the referenced discussion. It's arguing a specific syntax rather than trying to resolve any of the mentioned implementation issues. |
Beta Was this translation helpful? Give feedback.
-
What do you want @HaloFour an example of the expected IL? This is a long standing issue and I spoke with the team back when I implemented the |
Beta Was this translation helpful? Give feedback.
-
@HaloFour, Even in the hard case of two or more different tail calls intermingling the compiler could emit the correct and efficient code, especially if I can dynamically emit it based on passed expressions... Why should I make a nuget with some kind of special The only down side I see is an Assembly makes a braking change and removes or adds |
Beta Was this translation helpful? Give feedback.
-
I'd much prefer the proposed |
Beta Was this translation helpful? Give feedback.
-
@quinmars I don't see why the person should have to ever write With that typed, I don't see why a hybrid approach can't be taken, e.g. if the function is marked for tail then you ask the compiler to insert it for you otherwise you always can manually have |
Beta Was this translation helpful? Give feedback.
-
No. I just don't think it's necessary to open a second discussion that doesn't expand on anything from the referenced discussion. This isn't a proposal as much as it is a "me too!" The referenced discussion goes into a bunch of options with a bunch of ramifications. The syntax at this point seems like the least interesting aspect of the conversation, especially since syntax changes wouldn't be required to implement such a feature. |
Beta Was this translation helpful? Give feedback.
-
Doesn't expand on anything? I don't see anything about unrolling as an option in any of the other proposals. In addition to that this proposal is slightly different and could be combined with that one so I suppose I could just comment on the existing thread if that would appease you. I don't disagree with you that this is a "me too", nor that this even requires any syntax change as the compiler itself really should be able to figure this out... I guess my point is moot as somehow we are going to get contra variant returns before we had support for a 20 year old instruction which would have benefited a lot of people. |
Beta Was this translation helpful? Give feedback.
-
Having the compiler unroll the recursion is a part of #2304.
The JIT does it automatically where it is deemed appropriate. The Anyway, I'm not trying to say that I disagree with the value of tail calls, and it does seem to be on the team's radar given we have two separate discussions submitted by team members. It's my opinion that the conversation is best left to one place, but I'll leave that for the team to decide. |
Beta Was this translation helpful? Give feedback.
-
The example you've posted isn't tail recursive, it's more "tail recursion modulo cons". If you've flagged the method as tail would you expect the compiler to raise an error as the method isn't actually tail recursive? |
Beta Was this translation helpful? Give feedback.
-
@foxesknow, Error No, but Warning yes. I find it similar to an unused variable or marking a method as |
Beta Was this translation helpful? Give feedback.
-
@HaloFour that discussion outlines a way to not use While its in the same dimension it's not entirely the same thing as I said in my Plus 1 but if you and or the team feel that strongly about it I will move my comments to that discussion and take up my gripes there. This was specifically separated not to start a argument in someone else's proposal and referencing it from there seemed like the right thing to do at the time. |
Beta Was this translation helpful? Give feedback.
-
See also this GIST Debug GIST Prod which indicates that using the |
Beta Was this translation helpful? Give feedback.
-
@juliusfriedman, first I think the recursion is an implementation detail and shouldn't be at such a prominent position of the method signature. I do acknowledge though that there is already precedence by the tail void QuickSort<T>(T[] arr, int low, int high)
{
if (low >= high)
return;
var pivot = Partition(arr, low, high);
if (pivot - low < high - pivot)
{
QuickSort(arr, low, pivot - 1);
QuickSort(arr, pivot + 1, high);
Console.WriteLine("DEBUG");
}
else
{
QuickSort(arr, pivot + 1, high);
QuickSort(arr, low, pivot - 1);
}
} What should the compiler do with it? Give a warning, because the second call in the first void QuickSort<T>(T[] arr, int low, int high)
{
if (low >= high)
return;
var pivot = Partition(arr, low, high);
if (pivot - low < high - pivot)
{
QuickSort(arr, low, pivot - 1);
tail QuickSort(arr, pivot + 1, high);
// Console.WriteLine("DEBUG"); // uncommenting this line would be an error
}
else
{
QuickSort(arr, pivot + 1, high);
tail QuickSort(arr, low, pivot - 1);
}
}
It is very important, that the compiler can and will perform the tail call optimization, where the author intented it to be happened. This is not an ordinary optimization that brings you some speed improvements. If the optimization is omitted, your program will crash with a stackoverflow exception. Therefore I need guarantees and not good intentions, else I will rewrite the method without recursion myself. |
Beta Was this translation helpful? Give feedback.
-
Not wanting to be an ass, but his code is not a valid tail function. In the code above, the last term is That being said, I'd like to see the compiler using tail calls wherever possible (without any special syntax) - except if a method is marked with |
Beta Was this translation helpful? Give feedback.
-
I would expect the compiler to emit the tail at every call to I still think your implementation is a bit contrived because I would assume that all the calls to |
Beta Was this translation helpful? Give feedback.
-
…and:
Not all recursion is tail recursion, and not all recursion can be tail-call optimized. |
Beta Was this translation helpful? Give feedback.
-
I would expect the compiler to know what can and cannot be since it has all the context. |
Beta Was this translation helpful? Give feedback.
-
I think the point is that not all recursion within a single method should be a tail call. The working stack space of a quick sort implementation can be reduced from Scala has a similar method-wide annotation for requiring tail recursion, and as a result it's implementation of quick sort cannot use it. It has to rely on the compiler detecting that half of the calls could use tail recursion and for it to be applied automatically, which makes the solution much more fragile. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour I like to think of C# as more like C but perhaps that's the wrong way to think? (What about AOT) In C or C++ you don't have tail. I would argue that the C# compiler should be better than scala compiler... not a copy of it's mistakes |
Beta Was this translation helpful? Give feedback.
-
C and C++ don't have a lot of things that C# has. :)
I agree, which is why I don't really think that "tail" recursion should be enabled or enforced at the method level. Of the existing proposals/discussions I kind of prefer the Using the existing runtime facilities, whether that be the But that's just my opinion. |
Beta Was this translation helpful? Give feedback.
-
The next step would be then to apply that attribute (or use existing) when the compiler notices a recursive and it meets the correct criteria automatically. The problem would then be how to get it to turn it off when I don't want it which is the inverse problem and much harder to solve (required a don't tail keyword) |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Proposal
Add the
tail
keyword which will enforce that a.tail
instruction is emitted into the body of the underlying function at function calls to itself or other calls marked with thetail
keyword.The metadata could be encoded into a
modopts
,modreq
making it unusable from compilers which didn't understand it.Example
Alternative
BCL Attribute with compiler support
Plus 1
If you could come up with a source generator / analyzer and eventually compiler support for this optimization where I could get:
With some type of new
[Unrolled]
/ existing[AggressiveOptimization]
Attribute or otherwise.See also: #2544
I would oppose
unrolled
as a keyword but not strongly.Beta Was this translation helpful? Give feedback.
All reactions