Почему моя реализация, оптимизированная для хвостового вызова, быстрее, чем обычная рекурсивная, если она не поддерживается в c #? - PullRequest
0 голосов
/ 05 марта 2019

Недавно я познакомился с концепцией оптимизации хвостовых вызовов, и, насколько я могу судить, она не поддерживается в компиляторе .NET, но есть обходной путь, называемый «трамплининг», который я еще не использовал.

Как мои тесты SumRecTail могут быть немного быстрее, чем моя функция SumRec, если она не поддерживается? Учитывая, что у меня нет ошибки в моем тесте производительности

   public class Program
   {
    private static readonly int n = 100;
    private static readonly int[] a = Enumerable.Range(0, n).ToArray();


    static void Main(string[] args)
    {
        Mark("Marking regular recursion", SumRec);
        Mark("Marking tail recursion", SumRecTail);
        Console.ReadKey();
    }

    public static double Mark(string description, Func<int, double> method)
    {
        int noOfRuns = 30;
        int count = 100_000;
        double st = 0.0, sst = 0.0, dummy = 0.0;
        Console.WriteLine(description);
        for (int j = 0; j < noOfRuns; j++)
        {
            Timer t = new Timer();
            for (int i = 0; i < count; i++)
            {
                dummy += method(n);
            }
            double time = t.CheckNanoSeconds() / count;
            st += time;
            sst += time * time;
        }
        double mean = st / noOfRuns, sdev = Math.Sqrt((sst - mean * mean * noOfRuns) / (noOfRuns - 1));
        Console.WriteLine("avg {0} ns, {1} sdev", Math.Round(mean, 5), Math.Round(sdev, 5));
        return dummy / noOfRuns;
    }

    public static double Mark(string description, Func<int, int, double> method)
    {
        int noOfRuns = 30;
        int count = 100_000;
        double st = 0.0, sst = 0.0, dummy = 0.0;
        Console.WriteLine(description);
        for (int j = 0; j < noOfRuns; j++)
        {
            Timer t = new Timer();
            for (int i = 0; i < count; i++)
            {
                dummy += method(n, 0);
            }
            double time = t.CheckNanoSeconds() / count;
            st += time;
            sst += time * time;
        }
        double mean = st / noOfRuns, sdev = Math.Sqrt((sst - mean * mean * noOfRuns) / (noOfRuns - 1));
        Console.WriteLine("avg {0} ns, {1} sdev", Math.Round(mean, 5), Math.Round(sdev, 5));
        return dummy / noOfRuns;
    }

    private static double SumRecTail(int start, int sum)
    {
        return start == 0 ? sum : SumRecTail(start - 1, sum + start);
    }

    private static double SumRec(int end)
    {
        return end > 0 ? SumRec(end - 1) + end : 0;
    }
}

Класс таймера:

   public class Timer
{
    private readonly Stopwatch StopWatch = null;

    public Timer()
    {
        StopWatch = Stopwatch.StartNew();
    }

    public double CheckNanoSeconds()
    {
        return StopWatch.Elapsed.TotalMilliseconds * 1000000;
    }
}

Выход:

Marking regular recursion
avg 2044,21167 ns, 621,95915 sdev

Marking tail recursion
avg 1857,5378 ns, 69,5234 sdev
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...