【问题标题】:What is the benefit of SequenceEqual?SequenceEqual 有什么好处?
【发布时间】:2018-03-30 21:53:04
【问题描述】:

我在 for 循环中对 SequenceEqual 和元素比较进行了比较。

static void Main(string[] args)
    {
        //var myList = new List<short>();
        //var anotherList = new List<short>();
        var myList = new short[2000000];
        var anotherList = new short[2000000];

        for (int i = 0; i < 2000000; i++)
        {
            //myList.Add(5);
            //anotherList.Add(5);
            myList[i] = 5;
            anotherList[i] = 5;
        }

        var watch = System.Diagnostics.Stopwatch.StartNew();
        //for (int i = 0; i < 2000000; i++)
        //{
        //    if (myList[i] != anotherList[i])
        //        break;
        //}
        bool isEqual = myList.SequenceEqual(anotherList);
        watch.Stop();
        var elapsedMs = watch.ElapsedMilliseconds;
        Console.WriteLine(elapsedMs);
        Console.Read();
    }

当myList和anotherList为数组时,直接比较执行4ms,SequenceEqual执行21ms。当 myList 和 anotherList 为列表时,直接比较执行 13 ms,SequenceEqual 执行 30 ms。

如果它慢得多,是否有使用它有益的情况?我只能想到一个,它节省了几行代码。

【问题讨论】:

  • 它对类型使用默认的相等比较器,这不一定与您使用 == 得到的相同
  • @Crowcoder 我可以尝试使用 Equals,但我怀疑会有很大的不同。
  • 好吧,如果您在性能关键的应用程序中比较具有 200 万个项目的数组(碰巧也相等,这是最坏的情况),那么请确保您不应该使用 SequenceEquals。但在大多数其他实际应用程序中,它们之间的性能差异并不重要,而“节省几行”则是。
  • 但它将是身份转换(我的意思是像'source as T[]'之类的东西),所以应该很快,因为没有执行任何转换。
  • 我做了更多的测试。减速的很大一部分似乎在于SequenceEqualLists 视为IEnumerable 并不得不使用通用Enumerator。使用List 特定的Enumerator 可以使速度翻倍。

标签: c# performance linq ienumerable


【解决方案1】:

更具体地说,尤其是Lists,似乎很多减速来自SequenceEqual 中的通用实现,使用通用IEnumerator 而不是List 特定版本,这是两倍多快速地。但是,如果您无论如何要测试List,不妨直接在for 循环中编码,而不是使用枚举。

请注意,对IList 的测试要慢很多,因为List 没有实现IList&lt;T&gt;.operator[],这意味着它会调用IList.operator[],而IList.operator[] 会返回object 并导致装箱。

我也是特例Arrays,因为IList 接口比直接访问慢得多,同样是由于拳击。

当然,利用可用于已知类型的高速CountLength 使得不等长比较比SequenceEqual 快得多。 OTOH,如果前两个元素不相等,SequenceEqual 可以比我的函数更快。

这是我的LocalSequenceEqual

public static bool LocalSequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer = null) {
    if (first is ICollection<TSource> fc && second is ICollection<TSource> sc)
        if (fc.Count != sc.Count)
            return false;

    var cmp = comparer ?? EqualityComparer<TSource>.Default;

    if (first is TSource[] fa && second is TSource[] sa) {
        for (int j1 = 0; j1 < fa.Length; ++j1)
            if (!cmp.Equals(fa[j1], sa[j1]))
                return false;
        return true;
    }

    if (first is List<TSource> fl && second is List<TSource> sl) {
        for (int j1 = 0; j1 < fl.Count; ++j1) {
            if (!cmp.Equals(fl[j1], sl[j1]))
                return false;
        }
        return true;
    }

    using (var e1 = first.GetEnumerator()) {
        using (var e2 = second.GetEnumerator()) {
            while (e1.MoveNext()) {
                if (!(e2.MoveNext() && cmp.Equals(e1.Current, e2.Current)))
                    return false;
            }
            if (e2.MoveNext())
                return false;
        }
    }
    return true;
}

【讨论】:

  • 这是我想到的实现,感谢您的努力。还有其他几种类型可以从直接转换中受益(SortedList、OrderedDictionary),但它们不太常见,所以也许不值得。
  • @MBakardzhiev 在某些时候,开销超过了快速不相等情况的价值,而通用 SortedList&lt;TKey, TValue&gt;OrderedDictionary&lt;TKey, TValue&gt; 没有我看到的优化特殊情况。
猜你喜欢
  • 2011-01-04
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-05
相关资源
最近更新 更多