我最近介绍了尾部调用优化的概念,据我所知,.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