【问题标题】:C# - For vs Foreach - Huge performance differenceC# - For vs Foreach - 巨大的性能差异
【发布时间】:2013-02-18 17:47:33
【问题描述】:

i 正在对一种算法进行一些优化,该算法在给定数组中找到大于 X 的最小数字,但随后 a i 偶然发现了一个奇怪的差异。在下面的代码中,“ForeachUpper”结束于 625 毫秒,而“ForUpper”结束于,我相信,几个小时(慢得多)。为什么会这样?

  class Teste
{
    public double Valor { get; set; }

    public Teste(double d)
    {
        Valor = d;
    }

    public override string ToString()
    {
        return "Teste: " + Valor;
    }
}

  private static IEnumerable<Teste> GetTeste(double total)
    {
        for (int i = 1; i <= total; i++)
        {
            yield return new Teste(i);
        }
    }
    static void Main(string[] args)
    {
        int total = 1000 * 1000*30 ;
        double test = total/2+.7;

        var ieTeste = GetTeste(total).ToList();


        Console.WriteLine("------------");

        ForeachUpper(ieTeste.Select(d=>d.Valor), test);
        Console.WriteLine("------------");
        ForUpper(ieTeste.Select(d => d.Valor), test);
        Console.Read();
    }

    private static void ForUpper(IEnumerable<double> bigList, double find)
    {
        var start1 = DateTime.Now;

        double uppper = 0;
        for (int i = 0; i < bigList.Count(); i++)
        {
            var toMatch = bigList.ElementAt(i);
            if (toMatch >= find)
            {
                uppper = toMatch;
                break;
            }
        }

        var end1 = (DateTime.Now - start1).TotalMilliseconds;

        Console.WriteLine(end1 + " = " + uppper);
    }

    private static void ForeachUpper(IEnumerable<double> bigList, double find)
    {
        var start1 = DateTime.Now;

        double upper = 0;
        foreach (var toMatch in bigList)
        {
            if (toMatch >= find)
            {
                upper = toMatch;
                break;
            }
        }

        var end1 = (DateTime.Now - start1).TotalMilliseconds;

        Console.WriteLine(end1 + " = " + upper);
    }

谢谢

【问题讨论】:

  • 我相信这可能是重复的点击这里查看[重复][1] [1]:stackoverflow.com/questions/44220/…
  • 顺便说一句,使用 Stopwatch 类。
  • 你为什么要从breakif
  • @HighCore:这就是为什么我们会收到想要在 LINQ 中做所有事情的问题,即使 foreach 会更好。 不要忘记 for 和 foreach。只需了解何时使用您提供的每种工具即可。
  • @Alex 那是O(n*log(n)) 而不是O(n)。您无需对整个事物进行排序即可获得最小/最大值。哦,如果您先进行过滤,您会更快地执行所有操作,而不是在最后进行过滤。

标签: c# performance for-loop foreach


【解决方案1】:

IEnumerable&lt;T&gt; 不可索引。

您在 for 循环的每次迭代中调用的 Count()ElementAt() 扩展方法是 O(n);他们需要遍历集合以找到计数或第 n 个元素。

道德:了解你的收藏类型。

【讨论】:

  • Regardign Count() 值得注意的是它依赖于一个实际的集合类型,如果它实现了带有 Count 属性的 ICollection Count() 将使用它
  • @sll:ElementAt 也是如此。如果 OP 在Select 之后添加.ToArray(),那么巨大的差异就会消失。
  • 有趣的是,这意味着当源集合是可枚举的时,简单的 foreach 可以轻松地击败诸如快速排序/二进制搜索之类的算法。
  • @WoF_Angel:更准确地说,您不应将for 与任意IEnumerable&lt;T&gt;s 一起使用。
  • @WOF_Angel:实际上是相反的:快速排序和二进制搜索等算法依赖于可索引的集合,以提供它们所提倡的时间复杂度。使用可枚举永远不会使这样的算法运行得更快(实际上,使用普通数组/for 循环对集合的任何迭代几乎总是会更快)。
【解决方案2】:

造成这种差异的原因是您的for 循环将在每次迭代时执行bigList.Count()。在您的情况下,这确实很昂贵,因为它将执行 Select 并迭代完整的结果集。

此外,您正在使用 ElementAt 再次执行选择并将其迭代到您提供的索引。

【讨论】:

    猜你喜欢
    • 2010-10-15
    • 2019-04-21
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 1970-01-01
    • 2019-03-26
    相关资源
    最近更新 更多