Jak wspomniano w innych odpowiedziach, CLR obsługuje optymalizację połączeń końcowych i wydaje się, że w przeszłości podlegał stopniowym ulepszeniom. Jednak obsługa go w C # ma otwarty Proposal
problem w repozytorium git do projektowania języka programowania C # Obsługa ogona rekurencji # 2544 .
Możesz tam znaleźć przydatne szczegóły i informacje. Na przykład wspomniany @jaykrell
Podam to, co wiem.
Czasami wywołanie ogonowe to wynik korzystny dla wszystkich. Może oszczędzać procesor. jmp jest tańszy niż call / ret. Może oszczędzać stos. Dotykanie mniejszej ilości stosu zapewnia lepszą lokalizację.
Czasami tailcall to utrata wydajności, wygrana na stackach. Środowisko CLR ma złożony mechanizm, w którym przekazuje więcej parametrów do wywoływanego niż odebrany wywołujący. Mam na myśli konkretnie więcej miejsca na stosy dla parametrów. To jest powolne. Ale oszczędza stos. Zrobi to tylko z ogonem. prefiks.
Jeśli parametry wywołującego są większe niż parametry wywoływane, zwykle jest to dość łatwa transformacja korzystna dla wszystkich. Mogą istnieć czynniki, takie jak zmiana pozycji parametru z zarządzanej na liczbę całkowitą / zmiennoprzecinkową oraz generowanie dokładnych map stosu i tym podobnych.
Jest jeszcze jeden punkt widzenia - algorytmy, które wymagają eliminacji wywołań ogonowych, aby móc przetwarzać dowolnie duże dane ze stałym / małym stosem. Nie chodzi o wydajność, ale o zdolność do biegania.
Wspomnę również (jako dodatkowe informacje), gdy generujemy skompilowaną lambdę przy użyciu klas wyrażeń w System.Linq.Expressions
przestrzeni nazw, istnieje argument o nazwie „tailCall”, który, jak wyjaśniono w komentarzu, jest
Wartość logiczna, która wskazuje, czy optymalizacja wywołań ogona zostanie zastosowana podczas kompilowania utworzonego wyrażenia.
Jeszcze nie próbowałem i nie jestem pewien, jak może pomóc w związku z twoim pytaniem, ale prawdopodobnie ktoś może spróbować i może się przydać w niektórych scenariuszach:
var myFuncExpression = System.Linq.Expressions.Expression.Lambda<Func< … >>(body: … , tailCall: true, parameters: … );
var myFunc = myFuncExpression.Compile();
preemptive
(np. Algorytm silni) iNon-preemptive
(np. Funkcję Ackermanna). Autor podał tylko dwa przykłady, o których wspomniałem, nie podając właściwego uzasadnienia tego rozwidlenia. Czy to bifurkacja jest taka sama jak rekurencyjne funkcje ogona i nieogonowe?