【问题标题】:C# linq order by and other statements with foreach, is there a performance difference?C# linq order by 和 foreach 的其他语句,有性能差异吗?
【发布时间】:2014-12-15 03:12:42
【问题描述】:

我正在使用实体框架、集合以及其中还使用 where-conditions 和 order by actions 进行大量编程。我一直在问自己这个问题,但一直无法弄清楚。

假设我有以下两段代码;

示例 1:

// An unsorted string array.
string[] letters = { "d", "c", "a", "b" };
// Use LINQ query syntax to sort the array alphabetically.
var sorted = from letter in letters
         orderby letter
         select letter;

// Loop with the foreach keyword.
foreach (string value in sorted)
{
    Console.WriteLine(value);
}

示例 2:

// An unsorted string array.
string[] letters = { "d", "c", "a", "b" };

// Loop with the foreach keyword.
foreach (string val in letters.OrderBy(l => l))
{
    console.writeline(val)
}

第一个示例首先对结果集进行排序,然后遍历集合,第二个示例在我们将对其进行迭代时具有排序依据。现在我一直想知道的真正问题在哪里......性能有什么区别(如果有的话)?这两种方法中的一种是否比另一种更好?与 where 条件相同(简单和复杂的有连接),有什么显着的区别吗?

【问题讨论】:

  • 检查 IL,这些代码应该编译成非常相似(如果不完全相同)的代码。您的第一个示例仍然是延迟操作,因此只会在您迭代它时运行。第二个例子是第一个例子的扩展方法等价物。至于进一步的差异,您需要分析才能看到,但总的来说,性能不应该成为处理数据集的表现力的问题。如果性能是一个问题,请确定代码的热门跟踪,然后检查您对 Linq(以及更进一步的迭代器)的使用。在大多数情况下,预先做这件事是不值得的。
  • 没有显着差异,因为它几乎相同。前者被编译到方法语法中,无论您是否将查询分配给变量并不会真正影响性能。
  • 它是一样的 - OP 可悲地不知道 LINQ 延迟执行。这意味着排序在 foreach 中的两次发生。

标签: c# performance linq query-performance


【解决方案1】:

两者没有区别..

代码-

static void Main(string[] args)
{
    string[] letters = { "d", "c", "a", "b" };
    // Use LINQ query syntax to sort the array alphabetically.
    var sorted = from letter in letters
                    orderby letter
                    select letter;

    // Loop with the foreach keyword.
    foreach (string value in sorted)
    {
        Console.WriteLine(value);
    }

    foreach (string val in letters.OrderBy(letter => letter))
    {
        Console.WriteLine(val);
    }
}

生成的代码-

private static void Main(string[] args)
{
  string[] strArray1 = new string[4]
  {
    "d",
    "c",
    "a",
    "b"
  };
  string[] strArray2 = strArray1;
  if (Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate2 == null)
  {
    // ISSUE: method pointer
    Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate2 = new Func<string, string>((object) null, __methodptr(\u003CMain\u003Eb__0));
  }
  Func<string, string> keySelector1 = Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate2;
  foreach (string str in (IEnumerable<string>) Enumerable.OrderBy<string, string>((IEnumerable<string>) strArray2, keySelector1))
    Console.WriteLine(str);
  string[] strArray3 = strArray1;
  if (Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate3 == null)
  {
    // ISSUE: method pointer
    Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate3 = new Func<string, string>((object) null, __methodptr(\u003CMain\u003Eb__1));
  }
  Func<string, string> keySelector2 = Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate3;
  foreach (string str in (IEnumerable<string>) Enumerable.OrderBy<string, string>((IEnumerable<string>) strArray3, keySelector2))
    Console.WriteLine(str);
}

[CompilerGenerated]
private static string \u003CMain\u003Eb__0(string letter)
{
  return letter;
}

[CompilerGenerated]
private static string \u003CMain\u003Eb__1(string letter)
{
  return letter;
}

编辑: 这是我很想尝试的一个有趣的变体。在上面的例子中,对于查询表达式,编译器足够聪明,可以优化 Select away.. 但是,如果在第二个变体中,我们添加一个显式的 @987654324 呢? @。这并不奇怪,但编译器仍然没有优化掉显式的.Select(与查询表达式中的显式Select相比——编译器的要求)。

代码-

    foreach (string val in letters.OrderBy(letter => letter).Select(letter => letter))
    {
        Console.WriteLine(val);
    }

生成的代码-

  string[] strArray4 = strArray1;
  if (Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate6 == null)
  {
    // ISSUE: method pointer
    Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate6 = new Func<string, string>((object) null, __methodptr(\u003CMain\u003Eb__2));
  }
  Func<string, string> keySelector3 = Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate6;
  IOrderedEnumerable<string> orderedEnumerable = Enumerable.OrderBy<string, string>((IEnumerable<string>) strArray4, keySelector3);
  if (Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate7 == null)
  {
    // ISSUE: method pointer
    Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate7 = new Func<string, string>((object) null, __methodptr(\u003CMain\u003Eb__3));
  }
  Func<string, string> selector = Program.CS\u0024\u003C\u003E9__CachedAnonymousMethodDelegate7;
  foreach (string str in Enumerable.Select<string, string>((IEnumerable<string>) orderedEnumerable, selector))
    Console.WriteLine(str);

【讨论】:

  • @All,感谢您的所有回答!问题多次得到很好的回答,但由于生成代码的示例,我接受了这个问题。
【解决方案2】:

您的第一个查询相当于

... = letters.OrderBy(letter => letter).Select(letter => letter);

你的第二个查询是

... in letter.OrderBy(l => l)) {

两个查询几乎相同,只有第一个查询有一个额外的.Select(...) 调用,您可以在其中选择给定的输入,所以它毫无意义。 C# 编译器可能会删除该调用,但您必须查看生成的 IL 才能知道这一点。

此外,您的第一条语句不执行查询。 .OrderBy(...) 和大多数 linq 语句都是查询定义。这意味着您对.OrderBy(...) 的调用实际上并没有执行,它在您遍历结果之前没有得到回答。因此,在您的两个版本中,当foreach (... in &lt;collection&gt;) 访问将要迭代的集合时,都会执行查询。

结论:性能差异将非常小,以至于您必须非常努力才能发现任何真正的差异。当然,这是我的拙见。

【讨论】:

  • “第一个查询有一个额外的 .Select(...) 调用,您可以在其中选择给定的输入,因此它毫无意义” 没有它甚至不会在查询语法中编译(在 C# 中,而不是在 VB.NET 中)。
  • @TimSchmelter 正确。我不建议删除查询语法中的select ..,我只是指出从功能的角度来看它没有做任何有用的事情。
【解决方案3】:

我能看到的唯一区别是,在第一个代码中,Iteratorforeach 之前创建(未执行),并且您将其存储在局部变量中。在第二个代码中,foreach 调用 GetEnumarator 方法直接在迭代器上而不是局部变量上。但是当然这不会对性能产生任何影响。除此之外,由于可读性,我更喜欢第二个。

【讨论】:

    猜你喜欢
    • 2011-08-08
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 2016-01-21
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    • 1970-01-01
    相关资源
    最近更新 更多