咨询区
-
Jonathon Reinhart
请问 someEnumerable.Single(predicate);
和 someEnumerable.Where(predicate).Single();
到底哪一个执行的更快?
毕竟前者写法更短,更简洁,我个人感觉它就是 someEnumerable.Where(predicate).Single();
的快捷写法,毕竟 ReSharper
也是这么建议的。
.
回答区
-
Greg Gum
要想判断快慢,最好的办法就是做 基准测试
,下面是我的测试代码:
class Program
{
const int N = 10000;
volatile private static int s_val;
static void DoTest(IEnumerable<int> data, int[] selectors)
{
Stopwatch s;
// Using .Single(predicate)
s = Stopwatch.StartNew();
foreach (var t in selectors)
{
s_val = data.Single(x => x == t);
}
s.Stop();
Console.WriteLine(" {0} calls to Single(predicate) took {1} ms.",
selectors.Length, s.ElapsedMilliseconds);
// Using .Where(predicate).Single()
s = Stopwatch.StartNew();
foreach (int t in selectors)
{
s_val = data.Where(x => x == t).Single();
}
s.Stop();
Console.WriteLine(" {0} calls to Where(predicate).Single() took {1} ms.",
selectors.Length, s.ElapsedMilliseconds);
}
public static void Main(string[] args)
{
var R = new Random();
var selectors = Enumerable.Range(0, N).Select(_ => R.Next(0, N)).ToArray();
Console.WriteLine("Using IEnumerable<int> (Enumerable.Range())");
DoTest(Enumerable.Range(0, 10 * N), selectors);
Console.WriteLine("Using int[]");
DoTest(Enumerable.Range(0, 10 * N).ToArray(), selectors);
Console.WriteLine("Using List<int>");
DoTest(Enumerable.Range(0, 10 * N).ToList(), selectors);
Console.ReadKey();
}
}
结果还是很震惊的,.Where(predicate).Single()
大概比 someEnumerable.Single(predicate);
快两倍,我执行了两遍来排除掉是否有缓存因素的存在。
-
10000 calls to Single(predicate) took 7938 ms.
-
10000 calls to Where(predicate).Single() took 3795 ms.
-
10000 calls to Single(predicate) took 8132 ms.
-
10000 calls to Where(predicate).Single() took 4318 ms.
-
ken
我在想 Single(predicate)
和下面的实现有什么不同。
public static TSource Single<TSource (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
return Where(source, predicate).Single();
}
Where
的实现是返回 Enumerable.Iterator
, 但从源码中可以看出来当不同的线程也在调用 MoveNext
方法时,这里存在一个竞争情况。
下面是 ILSpy 反编译的代码:
switch (this.state)
{
case 1:
this.enumerator = this.source.GetEnumerator();
this.state = 2;
break;
case 2:
break;
default:
return false;
}
而当前的 Single(predicate)
并没有处理这种情况。
点评区
其实用 基准测试
和 查看源码
都是一个很好的方式。