Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Find()与FirstOrDefault()的性能比较_C#_.net_Performance_Linq - Fatal编程技术网

C# Find()与FirstOrDefault()的性能比较

C# Find()与FirstOrDefault()的性能比较,c#,.net,performance,linq,C#,.net,Performance,Linq,类似问题: 在具有单个字符串属性的简单引用类型的大序列中搜索Diana得到了一个有趣的结果 using System; using System.Collections.Generic; using System.Linq; public class Customer{ public string Name {get;set;} } Stopwatch watch = new Stopwatch(); const string diana = "Diana"

类似问题:

在具有单个字符串属性的简单引用类型的大序列中搜索Diana得到了一个有趣的结果

using System;
using System.Collections.Generic;
using System.Linq;

public class Customer{
    public string Name {get;set;}
}

Stopwatch watch = new Stopwatch();        
    const string diana = "Diana";

    while (Console.ReadKey().Key != ConsoleKey.Escape)
    {
        //Armour with 1000k++ customers. Wow, should be a product with a great success! :)
        var customers = (from i in Enumerable.Range(0, 1000000)
                         select new Customer
                         {
                            Name = Guid.NewGuid().ToString()
                         }).ToList();

        customers.Insert(999000, new Customer { Name = diana }); // Putting Diana at the end :)

        //1. System.Linq.Enumerable.DefaultOrFirst()
        watch.Restart();
        customers.FirstOrDefault(c => c.Name == diana);
        watch.Stop();
        Console.WriteLine("Diana was found in {0} ms with System.Linq.Enumerable.FirstOrDefault().", watch.ElapsedMilliseconds);

        //2. System.Collections.Generic.List<T>.Find()
        watch.Restart();
        customers.Find(c => c.Name == diana);
        watch.Stop();
        Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T>.Find().", watch.ElapsedMilliseconds);
    }
使用系统;
使用System.Collections.Generic;
使用System.Linq;
公共类客户{
公共字符串名称{get;set;}
}
秒表=新秒表();
const string diana=“diana”;
while(Console.ReadKey().Key!=ConsoleKey.Escape)
{
//Armor拥有1000k++客户。哇,这应该是一款非常成功的产品!:)
var客户=(从Enumerable.Range(0,1000000)中的i开始)
选择新客户
{
Name=Guid.NewGuid().ToString()
}).ToList();
Insert(999000,新客户{Name=diana});//将diana放在末尾:)
//1.System.Linq.Enumerable.DefaultOrFirst()
watch.Restart();
customers.FirstOrDefault(c=>c.Name==diana);
看,停;
WriteLine(“Diana是在{0}ms中使用System.Linq.Enumerable.FirstOrDefault()”,watch.ElapsedMilliseconds找到的);
//2.System.Collections.Generic.List.Find()
watch.Restart();
customers.Find(c=>c.Name==diana);
看,停;
WriteLine(“Diana是在{0}ms中找到的,带有System.Collections.Generic.List.Find()”,watch.ElapsedMilliseconds);
}

这是因为List.Find()中没有枚举器开销,还是因为这个加上其他什么


Find()
运行速度几乎是原来的两倍,希望.Net团队将来不会将其标记为过时。

我能够模拟您的结果,所以我对您的程序进行了反编译,
Find
FirstOrDefault
之间存在差异

首先是反编译程序。我将您的数据对象设置为anonmyous数据项,仅用于编译

    List<\u003C\u003Ef__AnonymousType0<string>> source = Enumerable.ToList(Enumerable.Select(Enumerable.Range(0, 1000000), i =>
    {
      var local_0 = new
      {
        Name = Guid.NewGuid().ToString()
      };
      return local_0;
    }));
    source.Insert(999000, new
    {
      Name = diana
    });
    stopwatch.Restart();
    Enumerable.FirstOrDefault(source, c => c.Name == diana);
    stopwatch.Stop();
    Console.WriteLine("Diana was found in {0} ms with System.Linq.Enumerable.FirstOrDefault().", (object) stopwatch.ElapsedMilliseconds);
    stopwatch.Restart();
    source.Find(c => c.Name == diana);
    stopwatch.Stop();
    Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T>.Find().", (object) stopwatch.ElapsedMilliseconds);
所以它在一个项目数组上迭代是有意义的,因为列表是数组的包装器

但是,
可枚举
类上的
FirstOrDefault
使用
foreach
迭代项目。这将使用迭代器访问列表并移动到下一步。我认为您看到的是迭代器的开销

[__DynamicallyInvokable]
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
  if (source == null)
    throw Error.ArgumentNull("source");
  if (predicate == null)
    throw Error.ArgumentNull("predicate");
  foreach (TSource source1 in source)
  {
    if (predicate(source1))
      return source1;
  }
  return default (TSource);
}
[[uuu\u DynamicallyInvokable]
公共静态TSource FirstOrDefault(此IEnumerable源,Func谓词)
{
if(source==null)
抛出错误。ArgumentNull(“源”);
if(谓词==null)
抛出错误。ArgumentNull(“谓词”);
foreach(源中的TSource source1)
{
if(谓词(source1))
返回源1;
}
返回默认值(TSource);
}
Foreach只是使用可枚举模式。看看这张图片

我点击foreach查看它在做什么,你可以看到dotpeek想带我去enumerator/current/next实现,这很有意义


除此之外,它们基本上是相同的(测试传入谓词以查看某个项是否是您想要的)

我打赌
FirstOrDefault
是通过
IEnumerable
实现运行的,也就是说,它将使用标准的
foreach
循环进行检查
List.Find()
不是Linq()的一部分,可能使用标准的
for
循环,从
0
Count
(或者另一个快速内部机制可能直接在其内部/包装数组上运行)。通过消除枚举的开销(并进行版本检查以确保列表未被修改),
Find
方法更快

如果添加第三个测试:

//3. System.Collections.Generic.List<T> foreach
Func<Customer, bool> dianaCheck = c => c.Name == diana;
watch.Restart();
foreach(var c in customers)
{
    if (dianaCheck(c))
        break;
}
watch.Stop();
Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T> foreach.", watch.ElapsedMilliseconds);
此方法的运行速度仅比
Find()
方法慢5.5%


所以底线是:通过数组元素循环比处理
foreach
迭代开销更快。(但两者都有各自的优点/缺点,所以只需选择在逻辑上对代码有意义的内容。此外,速度上的微小差异很少会导致问题,所以只需使用对可维护性/可读性有意义的内容即可)

FirstOrDefault
之前尝试计时
查找()。那么结果是什么呢?@Oded做到了。完全一样。我还连续运行了两次FirstOrDefault,但仍然是23-24毫秒(在我的iCore5上)。看起来没什么意思。性能是否与列表大小成线性关系(对于其他列表大小,FirstOrDefault是否总是需要两倍的时间,或者使用Linq是否需要固定的10毫秒成本)?在Mono上,性能甚至更高:Diana使用System.Collections.Generic.list.Find()在30毫秒内找到。Diana是在176毫秒内使用System.Linq.Enumerable.FirstOrDefault()找到的。每个项对
FirstOrDefault
进行三次间接调用,对
Find
进行一次间接调用。与foreach和for进行了很好的比较。我总是喜欢可维护性/可读性,而不是为每一纳秒的性能提升而奋斗的糟糕的家伙:)但是对于真正大的序列(特别是当代码在服务器上运行时),查找性能优化是一项常见的任务,而选择速度快两倍的代码使我决定继续使用List.Find()“对于重型作战,”阿尔曼,我基本上同意。在这种情况下,您需要有1000万个条目才能获得真正可感知的性能影响(几百毫秒)。真的,在这一点上,你们可能应该放弃这个O(n)迭代来进行一些O(1)查找,比如键入名称的字典。@chrisnclar,或者甚至是更好的算法O(对不起):)当他说Mono需要176毫秒时,我对另一条评论更为惊讶。这只适用于最简单的单一属性类。如果一台服务器上运行着1000个并发客户端(我们经常遇到类似的情况),那么即使有10000个真实客户,又会发生什么呢?这就是Linq、lambda、委托、迭代器、枚举、反射和其他使用C#使我们的生活更轻松的习惯用法的成本
//3. System.Collections.Generic.List<T> foreach
Func<Customer, bool> dianaCheck = c => c.Name == diana;
watch.Restart();
foreach(var c in customers)
{
    if (dianaCheck(c))
        break;
}
watch.Stop();
Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T> foreach.", watch.ElapsedMilliseconds);
//4. System.Collections.Generic.List<T> for loop
var customersArray = customers.ToArray();
watch.Restart();
int customersCount = customersArray.Length;
for (int i = 0; i < customersCount; i++)
{
    if (dianaCheck(customers[i]))
        break;
}
watch.Stop();
Console.WriteLine("Diana was found in {0} ms with an array for loop.", watch.ElapsedMilliseconds);