我有一个包含两种方法的控制台应用程序:
public static IEnumerable<TSource>
FooA<TSource>(this IEnumerable<IEnumerable<TSource>> source)
{
return source.Aggregate((x, y) => x.Intersect(y));
}
public static IEnumerable<TSource>
FooB<TSource>(this IEnumerable<IEnumerable<TSource>> source)
{
foreach (TSource element in source.First())
{
yield return element;
}
}
它的作用:两者都采用一系列序列,FooA
产生所有序列的交集,然后返回结果。 FooB
只是迭代第一个序列。
我不明白: FooB
比FooA
慢10倍以上,而FooB
实际上要简单得多(没有)致电Intersect()
方法)。
结果如下:
00:00:00.0071053 (FooA)
00:00:00.0875303 (FooB)
通过直接返回FooB
, source.First()
可以快得多,无论如何我使用ILSpy反编译Distinct
方法并找到完全相同的foreach yield return loop:
private static IEnumerable<TSource> DistinctIterator<TSource>
(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
Set<TSource> set = new Set<TSource>(comparer);
foreach (TSource current in source)
{
if (set.Add(current))
{
yield return current;
}
}
yield break;
}
另外:在代码中我使用i无法返回source.First()
(我得到CS1622)。我在这里展示的实际上是一个更简单的代码,我将其拆除进行调试。
以下是我用于测试的代码:
List<List<int>> foo = new List<List<int>>();
foo.Add(new List<int>(Enumerable.Range(0, 3000*1000)));
Stopwatch sa = new Stopwatch();
sa.Start();
List<int> la = FooA(foo).ToList();
Console.WriteLine(sa.Elapsed);
Stopwatch sb = new Stopwatch();
sb.Start();
List<int> lb = FooB(foo).ToList();
Console.WriteLine(sb.Elapsed);
答案 0 :(得分:5)
您测量如此巨大差异的原因是Aggregate调用只返回您的初始列表,因为没有要汇总的项目,因为您的列表只有一个项目。
如果您将其更改为
List<List<int>> foo = new List<List<int>>()
{
new List<int>(Enumerable.Range(0, 3000 * 1000)),
new List<int>(Enumerable.Range(0, 3000 * 1000)),
};
只有一件像你一样:
A: 00:00:00.0037843
B: 00:00:00.0514177
但有两个项目:
A: 00:00:00.2130628
B: 00:00:00.0574932
现在A慢得多。第一个示例的不同之处在于阵列分配确实导致了更多的CPU周期。
AllocationAmount AllocationKind
B 1CAE0 Small
B 21E5C Small
B 20020 Large
B 40020 Large
B 80020 Large
B 100020 Large
B 200020 Large
B 400020 Large
B 800020 Large
B 1000020 Large
A B71B20 Large
这是垃圾收集器发出的GC AllocationTick ETW事件。实际上你确实比较了苹果和橙子。你的聚合调用基本上什么也没做。
答案 1 :(得分:0)
改为使用:
public static IEnumerable<TSource> FooB<TSource>(this IEnumerable<IEnumerable<TSource>> source) {
yield return source.First();
}
答案 2 :(得分:0)
FooA
根本不会致电Intersect
。序列中只有一个元素。 Aggregate
只是返回它。没有什么可以聚合的。
FooB
遍历第一个序列的所有元素。这需要时间。它只需要像FooA
那样返回第一个序列就需要更长的时间。