最近有件事引起了我的好奇心。
是Enumerable.Any(Func<TSource, bool> predicate)
方法这么慢吗比手动foreach,做同样的事情?
我一直在搞乱一些基准,并想到了这个。我正在检查List<int>
包含和项目,大约在列表的一半。
以下是我对几个不同大小的列表的测试结果:
条目:1 000,搜索条目:543
Alloc比 | ||||||||
---|---|---|---|---|---|---|---|---|
任何 | 4.05 | NA |
编译器可以优化使用foreach中的List<T>
(与IEnumerable<T>
相比)。我将无法解释细节,但如果你检查生成的IL(例如在sharplab.io),你已经会看到差异-编译器可以调用List<T>.Enumerator
上的具体方法,而不是通过callvirt
的多态调用(call和Callvirt)。不确定这(以及由于通过接口使用struct List<T>.Enumerator
而导致的一次性分配)会导致这样的性能差异。运行时可能会对它进行更多的优化(在sharplab上查看JIT Asm的差异)。(如果你想尝试更深入)。
如果您检查Enumerable.Any
的源代码,您将看到它使用相同的foreach
循环,差异归结为使用IEnumerable
接口:
public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
if (predicate == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.predicate);
}
foreach (TSource element in source)
{
if (predicate(element))
{
return true;
}
}
return false;
}
所以,正如@Jon Skeet在评论中正确诊断的那样,区别在于使用list和enumerable。
正如Jon Skeet在评论中建议的那样,我尝试将_items
集合从List<int>
更改为IEnumerable<int>
,以使比较公平。简而言之,这似乎是关键的区别。我的Foreach似乎是利用了这样一个事实,即它知道_items
集合是List<T>
,而Enumerable.Any
方法是IEnumerable<int>
。
下面是测试结果:
条目:1 000,搜索条目:543
Alloc比 | 1.00 | 1.00 |
---|