【问题标题】:Optimize LINQ for IList为 IList 优化 LINQ
【发布时间】:2011-08-17 06:39:33
【问题描述】:

不久前,我编写了一个IList 扩展方法,通过使用索引来枚举列表的一部分。在重构时,我意识到可以通过调用Skip(toSkip).Take(amount) 来执行类似的查询。在对此进行基准测试时,我注意到Skip 没有针对IList 进行优化。通过谷歌搜索,我最终找到了 Jon Skeet 的一个帖子,discussing why optimizing methods like Skip is dangerous

据我对文章的理解,问题是在修改集合时优化方法中没有抛出异常,但作为评论指出msdn文档本身冲突。

IEnumerator.MoveNext():

如果对集合进行了更改, 例如添加、修改或删除 元素,枚举数是 不可恢复地失效和下一个 调用 MoveNext 或 Reset 会抛出一个 无效操作异常。

IEnumerator.GetEnumerator():

如果对集合进行了更改, 例如添加、修改或删除 元素,枚举数是 不可恢复的无效及其 行为未定义

我看到了这两种约定的优点,对于是否优化有点迷茫。什么是合适的解决方案?正如 Kris Vandermotten 在 cmets 中提到的那样,我一直在考虑采用AsParallel()IList.AssumeImmutable() 方法。是否已经存在任何实现,或者这是一个坏主意?

【问题讨论】:

  • 如果 Jon 得出结论认为优化 Skip 等是错误的,并且有一段解释为什么......那么我会认为这是一个很好的理由。
  • 我认为“未定义”语义比“抛出异常”语义更实用(尽管后者更可靠),仅仅是因为后者要求每个枚举器实例都能够测试修改主机容器。啊!
  • 然而,Jon 的观点仍然成立。没有什么可以阻止您使用Skip/Take 编写ItemsInRange(Lo, Hi) 枚举器没有
  • @Rafe:我写了一个答案并删除了它,因为对称ItemsInRange 应该存在于IEnumerable 并且会抛出,所以IList 版本不能被优化乔恩的论点。
  • @Marc:我们怎么知道 Jon 在重新思考时是对的,而不是一开始就错了?

标签: c# .net linq optimization skip


【解决方案1】:

我同意 Rafe 的观点,即未定义的行为更正确。只有版本化的集合可以抛出异常,并不是所有的集合都是版本化的(数组是最大的例子)。如果您在对 MoveNext 的调用之间进行了 2^32 次更改,即使是版本化的集合也可能出现异常。

假设您真的关心版本控制行为,解决方案是为IList 获取Enumerator 并在每次迭代时调用MoveNext

    public static IEnumerable<T> Skip<T>(this IList<T> source, int count)
    {
        using (var e = source.GetEnumerator())
            while (count < source.Count && e.MoveNext())
                yield return source[count++];
    }

通过这种方式,您可以通过索引获得 O(1) 行为,但您仍然会获得调用 MoveNext 的所有异常抛出行为。请注意,我们只为异常副作用调用MoveNext;我们忽略它枚举的值。

【讨论】:

  • 有趣的解决方案!看起来有点骇人听闻的感觉其实很有道理。
【解决方案2】:

ReadOnlyCollection 类可能有助于您的不可变集合。

我的建议:除非您遇到性能问题,否则我个人不会尝试“欺骗”编译器。你永远不会知道,下一个版本可能会使优化后的代码运行速度比原来的慢一倍。不要抢先优化。框架中提供的方法可以生成一些很难重新实现的真正优化的代码。

here 是来自 msdn 的一篇文章,它提供了有关将哪些集合用于不同目的的信息。我会为任务使用适当的集合,而不是尝试优化 Skip and Take。

【讨论】:

  • 我不明白你的回答与问题有什么关系。
  • “问题是下一行执行的代码可能不是对 MoveNext() 的调用”需要澄清一下吗?
  • @Steven - 并不是要发布第一段。我开始回答,以为你在问别的东西。我已对其进行了更新以删除该文本。
  • 我真的需要在睡前停止回答问题。
猜你喜欢
  • 1970-01-01
  • 2011-02-26
  • 1970-01-01
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
相关资源
最近更新 更多