为什么Skip-extension方法没有优化?

时间:2014-06-03 10:21:26

标签: c# performance linq

我最近想知道为什么Enumerable - {@ 1}},SkipSkipWhileTakeWhile - 扩展方法未进行优化,以便使用for - 循环而不是枚举所有元素。

ElementAt等其他方法首先检查序列是否可以转换为IList<T>,然后使用索引器更快地找到元素。与FirstLastCount等类似,他们首先检查IEnumerable<T>是否可以转换为支持更快查找的集合。

为什么Skip方法没有以这种方式优化?

以下是我从ILSpy(。NET 4)获得的内容,SkipIterator用于所有跳过方法,但类似的代码用于TakeWhile

// System.Linq.Enumerable
private static IEnumerable<TSource> SkipIterator<TSource>(IEnumerable<TSource> source, int count)
{
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (count > 0 && enumerator.MoveNext())
        {
            count--;
        }
        if (count <= 0)
        {
            while (enumerator.MoveNext())
            {
                yield return enumerator.Current;
            }
        }
    }
    yield break;
}

正如您所看到的,如果可能,它甚至不会尝试使用for - 循环。

为什么代码与此不相似?

public static IEnumerable<TSource> MySkip<TSource>(this IEnumerable<TSource> source, int count)
{
    // check if it's a list or array to use a for-loop
    IList<TSource> listT = source as IList<TSource>;
    if (listT != null)
    {
        if (listT.Count <= count) yield break;
        for(int i = count; i < listT.Count; i++)
            yield return listT[i];
    }

    // no list or array, fallback to .NET method
    foreach (TSource t in source.Skip(count))
        yield return t;
}

我还针对默认的Skip测试了这个扩展方法,这里是无意义的查询,它检查列表中有多少元素有一个跟随者(总是count-1),但它应该做的工作:< / p>

Random rnd = new Random();
var randomNumbers = Enumerable.Range(1, 100000)
    .Select(i => new { Number = rnd.Next(100) })
    .ToList();

Stopwatch sw1 = new Stopwatch();
sw1.Start();

int countAllButLast = randomNumbers
    .TakeWhile((s, i) => randomNumbers.Skip(i).Any())
    .Count();

sw1.Stop();
Console.WriteLine("Time for default Skip with foreach: {0} Result: {1}", sw1.Elapsed, countAllButLast);

Stopwatch sw2 = new Stopwatch();
sw2.Start();

countAllButLast = randomNumbers
    .TakeWhile((s, i) => randomNumbers.MySkip(i).Any())
    .Count();

sw2.Stop();
Console.WriteLine("Time for MySkip with for-loop: {0} Result: {1}", sw2.Elapsed, countAllButLast);

性能差异巨大:

Time for default Skip with foreach:     00:01:16.5630092 Result: 99999
Time for MySkip with for-loop:          00:00:00.0334746 Result: 99999

0 个答案:

没有答案