递归与迭代速度不一致

时间:2014-08-14 04:24:06

标签: c# performance recursion iteration

我制作了一个程序来比较迭代速度和递归速度,以实现非常简单的操作。 我注意到一些奇怪的东西,我无法解释。 根据处理的值的数量,迭代或递归更快。但它并不一致。

  • 大约10,迭代更快。
  • 大约100,迭代更快。
  • 大约200,递归更快。
  • 大约1000,递归更快。
  • 大约5000,迭代更快。
  • 大约10000,迭代更快。
  • 除此之外,递归会进入堆栈溢出,因此无法测试任何更高的值。

对于较低和较高的可测试数量,迭代过程如何更快,但不适用于介于两者之间的那些?

我对此缺乏更深入的了解,如果有人能向我解释,我将不胜感激。

以下是那些想要自己测试的代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Compare_Recursive_Iterative
{
    class Program
    {
        static void Main(string[] args)
        {
            // Amount of values processed.
            long testNumber = 200;
            // Variables to measure time taken.
            double timeIterative, timeRecursive, timeTotal;

            // Iterative process + time elapsed calculation
            DateTime start = DateTime.Now;
            long retVal = 0;
            for(long i = 1; i <= testNumber; i++)
            {
                retVal = 0;
                for (long x = 1; x <= i; x++)
                { 
                    retVal += x;
                }
                Console.WriteLine("For " + i + ": " + retVal);
            }
            TimeSpan timeDiff = DateTime.Now - start;
            timeIterative = timeDiff.TotalMilliseconds;

            // Recursive process + time elapsed calculation
            DateTime start2 = DateTime.Now;
            for (long i = 1; i <= testNumber; i++)
            {
                Console.WriteLine("For " + i + ": " + calculate(i));
            }
            timeDiff = DateTime.Now - start2;
            timeRecursive = timeDiff.TotalMilliseconds;

            // Results to console.
            Console.WriteLine("Iterative: " + timeIterative + "ms /// Recursive: " + timeRecursive + "ms");
            timeDiff = DateTime.Now - start;
            timeTotal = timeDiff.TotalMilliseconds;
            Console.WriteLine("Total time needed: " + timeTotal + "ms");
            Console.WriteLine("Iterative - Recursive: " + (timeIterative - timeRecursive) + "ms");
            Console.Read();
        }


        // Recursive method
        static long calculate(long x)
        {
            if (x <= 1)
                return x;
            else
                return x + calculate(x - 1);
        }

    }
}

1 个答案:

答案 0 :(得分:4)

大部分时间都花费在你的样本中的Console.WriteLine中,如果你想测量的话,你需要为它的工作做一些额外的工作,所以你的措施没有意义然后

1)确保你只测量你想要测量的东西(不要写入任何输出,你在测试后这样做)

2)至少使用秒表,DateTime.Now根本不准确

3)将两个测试分别用于自己的功能,并在测试之前每个测试一次,以确保您不测量预热时间。

4)将两个函数调用相当长的时间(可能是1000?),然后记下每个函数的最小/最大/平均时间

5)确保您没有在系统上执行任何其他操作,并且没有任何密集型操作(在运行时禁用防病毒等)。

6)同样非常重要,不要在调试模式下进行测试,切换到第一个发布!在某些情况下,调试模式可以大大减慢速度,而在其他情况下几乎没有,这意味着在调试中函数A可能比B快得多,而在发布时则相反。也不要使用附带的调试器进行测试,只需从文件夹中启动exe。

因为你不是在做第1步,因为所有的时间都花在你自己的代码之外,所以导致你的问题是什么,其他的点是所以你可以想到这一切而不是回去和与Q&amp;在这里。

制作一个样本给你看,可能不完美但更接近基准应该是什么样子,还要注意你可能想把testiterative放回主代码中,如果你真的想比较它的内联(vs递归哪个)必须是一个功能)

void Main()
{
var TargetNumber = 2000;
var TestRuns = 10;
//warmup both methods
calculate(100);
TestIterative(100);

Stopwatch sw = new Stopwatch();

var RecursiveTimes = new List<long>();

for(int run = 1;run<=TestRuns;run++)
{
    sw.Restart();
    for (int i = 1; i <= TargetNumber; i++)
    {
        calculate(i);
    }
    sw.Stop();
    RecursiveTimes.Add(sw.ElapsedMilliseconds);
}

var IterativeTimes = new List<long>();

for(int run = 1;run<=TestRuns;run++)
{
    sw.Restart();
    for (int  i = 1; i <= TargetNumber; i++)
    {
        TestIterative(i);
    }
    sw.Stop();
    IterativeTimes.Add(sw.ElapsedMilliseconds);
}

Console.WriteLine("Iterative : " + IterativeTimes.Average() + " ms on average. Min and max : " + IterativeTimes.Min() + " / " + IterativeTimes.Max());
Console.WriteLine("Recursive : " + RecursiveTimes.Average() + " ms on average. Min and max : " + RecursiveTimes.Min() + " / " + RecursiveTimes.Max());  
}

static long TestIterative(long x)
{
    long retVal = 0;
    for (long y = 1; y <= x; y++)
    { 
        retVal += y;
    }
    return retVal;
}

static long calculate(long x)
{
    if (x <= 1)
        return x;
    else
        return x + calculate(x - 1);
}

另请注意,我将要测试的数字(从0到TargetNumber)与测试运行次数分开,因为没有理由将它们链接起来(TestRuns),这样您就可以多次测试一个小集合或者测试一个大集合几次。

正如您所看到的那样,所有输出都移到了程序的末尾,因为它很昂贵并且您不想测量它,即使将时间添加到列表也是在定时之外完成的区域和每次测试的时间(不在测试中)因为时间太小而且我们最终会有大量的零。

另一个好的基准测试是用可用的数据测试实际可用的代码,不要尝试微观基准测试&#34;没有做任何事情的代码,你无法确定在硬件上做几次添加所花费的时间,而这些硬件每秒会产生数十亿次。

一个好的经验法则,除非它使你的代码更加复杂,总是去迭代,而不是为了性能(但它确实更好),但主要是因为如果你没有理由给自己堆栈溢出可以避免它。