【发布时间】:2020-01-02 18:21:54
【问题描述】:
简介
今天在测试某些 LinQ 函数的性能差异时,我注意到 LastOrDefault(predicate) 几乎总是比 FirstOrDefault(predicate) 快,这让我有点感兴趣,所以我写了一些测试用例来测试这两个函数.
我创建了一个从 1 到 1 百万的 integer 值列表,如下所示:
List<int> l = new List<int>();
for (int i = 1; i <= 1000000; i++)
{
l.Add(i);
}
然后写了2个方法first()和last()
static void first(List<int> l)
{
int e = l.FirstOrDefault(x => x == 500000);
int z = l.FirstOrDefault(x => x == 500000);
int d = l.FirstOrDefault(x => x == 500000);
int v = l.FirstOrDefault(x => x == 500000);
int f = l.FirstOrDefault(x => x == 500000);
}
我在 for 循环中运行了 1000 次,同时设置了一个断点,条件是在最后一次迭代后停止,但在我测试的每个单一情况下,LastOrDefault 都更快。
测试用例
- 两者都设置为列表中间的一个元素(
LastOrDefault的速度几乎是原来的两倍) - 两者都设置为列表中具有相同距离的元素(如 250k 和 750k) - 同样
LastOrDefault更快 - 切换我的方法调用的顺序(在
First()之前调用Last())- 没有区别 - 使用各自的列表运行,而不是在同一个列表上运行(同样没有区别)
- 运行一个,关闭应用程序,重新打开,运行另一个(仍然
LastOrDefault更快) - 将谓词设置为不在列表中的元素(仍然相同)
- 将谓词更改为多个符合条件的对象(仍然相同)
-
创建一个降序列表而不是一个升序列表(没有区别)
.Net Core 版本:3.0
由于调试器可能不准确,我再次尝试使用此代码:
Console.WriteLine(DateTime.Now + ":" + DateTime.Now.Millisecond);
for (int i = 0; i < 1000; i++)
{
first(l);
}
Console.WriteLine(DateTime.Now + ":" + DateTime.Now.Millisecond);
for (int i = 0; i < 1000; i++)
{
last(l);
}
Console.WriteLine(DateTime.Now + ":" + DateTime.Now.Millisecond);
FirstOrDefault 也返回 34 秒,LastOrDefault 也返回 23 秒
问题
为什么LastOrDefault 在我所有的测试用例中都比FirstOrDefault 快得多?
【问题讨论】:
-
结果到底是什么?你是怎么测试的?是否留出了热身时间?
-
由于可枚举是一个列表,我非常怀疑您在列表中行走的方向是否存在任何性能差异。它是
0..end或end..0。没有别的了。 -
可能是因为 FirstOrDefault 使用了枚举器和 MoveNext,而 LastOrDefault 使用了逆 for 循环(用于 List)。实际上取决于类型以及它是否具有谓词。
-
@Patrick Hofman 我设置了一个 net Core Console 应用程序,填充列表,然后设置断点,然后在 for 循环中调用这两个方法 1000 次,并在到达时花费了我的调试器返回的时间最后一次迭代,“FirstOrDefault”为 32.8 秒,“LastOrDefault”为 16.7 秒 @Caramiriel 添加了网络核心信息
-
@steve16351 我想这一定是它。 .NET Standard 没有
Last的优化,Last所用的时间大约是您预期的两倍,但我认为与 .NET Core 中的 OP 相同。
标签: c# performance linq .net-core