【问题标题】:What is a good way to to indicate an IEnumerable is "slow" or "fast"?指示 IEnumerable 是“慢”还是“快”的好方法是什么?
【发布时间】:2012-06-07 15:05:22
【问题描述】:

What is the expected performance of IEnumerable? 的答案说没有办法知道迭代任意 IEnumerable 的性能。每次迭代都可以访问数据库或进行网络服务调用;或者它可能只返回数组/列表中的下一项。

鉴于此,有没有一种表示“这很快”的好方法?例如,使用T[]List<T> 而不是IEnumerable<T> 我知道从T[i]T[i+1] 会很快。 (当然,强制枚举返回列表/数组可能会产生其他性能问题。List<T> 还公开了可编辑语义。)

相反,返回IQueryable<T> 而不是IEnumerable<T> 是表示“这很慢”的好方法吗?或者IEnumerable<Task<T>>

C 的客户无法知道IEnumerable<T>s 将具有截然不同的性能特征。

class C
{
   readonly T[] m_data;
   public IEnumerable<T> ThisWillIterateQuickly { get { return m_data; } }

   public IEnumeralbe<T> ThisWillIterateSlowly
   {
      get
      {
         T retval = ... an expensive database call ...;
         yield return retval;
      }
   }

   public IQueryable<T> IsThisBetterForSlow { get { return ThisWillIterateSlowly; } }
   public T[] IsThisAGoodWayForFast { get { return m_data; } }
 }

【问题讨论】:

  • 这可能听起来很模糊,但只有如果您认为它不够足够快,它就会很慢。
  • 没有标准的广告机制。也许只是:文档?
  • Re IQueryable... 数组可以作为 IQueryable 公开 - 它没有说明性能
  • 鉴于您似乎暗示您有多个包含相同数据的数据结构,并且您需要区分快速结构和慢速结构......您可能想看看为什么您正在这样做,并且一直使用更快的结构?
  • @LukeH 当然,当IReallyFastEnumerable&lt;&gt; 导致性能问题并且您最初忽略它时,这一切都会分崩离析,因为您认为它确实按照锡上所说的那样......虽然很可爱;-)

标签: .net performance ienumerable iqueryable


【解决方案1】:

相反,返回 IQueryable 而不是 IEnumerable 是否是表示“这很慢”的好方法?

不,IQueryable&lt;T&gt; 固有于IEnumerable&lt;T&gt;... 而IEnumerable&lt;T&gt;T 的问题在于它可能是IQueryable&lt;T&gt; 在迭代时具有巨大的副作用,例如查询远程数据库或类似的东西。

是不是很有趣?

您可能应该询问IEnumerable&lt;T&gt; V.S. List&lt;T&gt; 其中第二个肯定有数据,不需要从其他地方获取。

【讨论】:

  • @丹。 IQueryable 通常会访问数据库,但不必这样做,它可以从其他地方获取它的值,或者将其保存在其中。但是是的,IQueryable&lt;T&gt; 表示有东西,比IEnumerable&lt;T&gt; 更好的副作用。
【解决方案2】:

如果您想保证迭代不会包含任何意外,那么我同意,公开一个T[] - 它的枚举器不能被覆盖,因为您不能从数组继承。迭代也是无副作用的,IEnumerable&lt;T&gt; 则不能这样说。

但是,我不同意公开数组表达这条信息,这更重要(在我看来)。东西的性能永远无法真正用代码来表达,除非你开始命名东西 CheapIterationExpensiveIteration

另一方面,使用数组,您只需将按需迭代的性能问题转移到填充数组的位置。这是保证完全解决性能问题的,因为它将是提供数组内容的任何内容的完整迭代。在IEnumerable&lt;T&gt; 中,如果迭代停止,性能问题也会停止 - 最快的代码是不运行的代码。

【讨论】:

  • @Dan 将其强制为T[] 涉及完整的迭代,这可能会很昂贵。使用T[],您还将失去延迟执行并强制运行整个可枚举。此外,返回 T[] 而不是 IEnumerable&lt;T&gt; 表达了不同的意图,人们会有不同的期望。
  • @Dan 我不同意这样的事情应该在 BCL 中。我个人什至不希望它出现在我自己的代码中。如果我暴露东西来表达我希望它如何被消费。数组表达很少,除了“这里是你几乎可以做你想做的事情的集合”。 IEnumerable&lt;T&gt; 对我说“我要被迭代”。在我看来,无论是快还是慢,都不应该是用代码表达的问题。也就是说,这是一个有趣的话题。返回一个数组将隐式表示迭代很快,因为没有其他东西可以插入。
  • @Dan 您现在可以从代码中看出这一点,还是您学习框架、阅读文档和配置文件来告诉您?我目前只从代码中得到它,因为它很明显,比如 Linq to SQL。也就是说,如果您想通过代码表达这一点,那么 LukeH 的评论可能是最好的解决方案。另一方面是,30 秒后,当他们运行应用程序并且它滞后时,他们会发现它很慢并且会适应。然后它变成了关于在你的 API 中实现一个约定的讨论,它只会为消费者节省一分钟左右的开发时间,但会在你的 API 中保留更长时间。
  • @Dan 但是 UI 会请求一个数组,此时,除非它在线程准备中完成,否则该数组将被填充,这仍然会阻塞 UI。或者就像我之前说的,您只需将费用转移到其他地方,此时 UI 必须再次意识到它才能相应地工作。我看到的问题是,你什么时候停止尝试用代码表达某些东西的文档?您选择用代码记录性能,例如,为什么不记录它涉及数据库或网络的事实。
  • 如果 UI 不想冒险,那么默认情况下它应该线程化工作。如果有一百万个项目给它一个数组怎么办?有太多的潜在点会破坏惯例或无法完全解决问题。我会把它留给消费者,你唯一的责任就是尽可能优化数组加载或可枚举迭代。
【解决方案3】:

再想一想之后,问题/问题似乎真正集中在IEnumerator.MoveNext() 的行为/性能上。使用 Visual Studio 2012,我能够创建 IEnumeratorIEnumerable 的异步版本:

public interface IAsyncEnumerator<T> : IDisposable
{
    Task<T> CurrentAsync { get; }
    Task<bool> MoveNextAsync();
    Task ResetAsync();
}

public interface IAsyncEnumerable<T>
{
    IAsyncEnumerator<T> GetAsyncEnumerator();
}

这种方法的一个缺点是没有很多语言支持;以上不适用于foreach。但是,扩展方法可以减轻痛苦:

public static class EnumeratorExtensions
{
    public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        using (var enumerator = enumerable.GetEnumerator())
        {
            while (enumerator.MoveNext())
                action(enumerator.Current);
        }
    }

    public static async Task ForEachAsync<T>(this IAsyncEnumerable<T> enumerable, Action<T> action)
    {
        using (var enumerator = enumerable.GetAsyncEnumerator())
        {
            while (await enumerator.MoveNextAsync())
                action(await enumerator.CurrentAsync);
        }
    }
}

【讨论】:

  • 只是想知道,为什么CurrentAsyncTask&lt;T&gt;,工作肯定是在MoveNextAsync 中完成的?所以你可以有T Current { get; }
  • 公平地说,这对我来说似乎并不直观,因为我希望属性获取器很快。如果消费者实际上并不想要下一件商品,为什么他们会下一步行动呢?在我看来,MoveNextAsync 总是做这项工作更有意义。
【解决方案4】:

我通常使用返回“快速”枚举的属性和返回“慢速”的方法。

您的问题是尽可能使用异步方法和设计的论据。然后,枚举需要多长时间并不重要,因为 UI 是响应式的并且用户很高兴 ;-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 2011-05-14
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多