如果c#不支持我的尾部调用优化实现,为什么比常规递归更快?

时间:2019-03-05 17:47:36

标签: c# asp.net-core benchmarking compiler-optimization tail-call-optimization

我最近介绍了尾部调用优化的概念,据我所知,.NET编译器并不固有地支持它,但是有一个尚未解决的称为“ Trampolining”的变通方法。

如果我的“ 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

0 个答案:

没有答案