【问题标题】:Is there a more efficient way to write this loop? [duplicate]有没有更有效的方法来编写这个循环? [复制]
【发布时间】:2020-10-22 10:46:56
【问题描述】:

我有以下循环迭代很长时间,queryResult 有 397464 行,每行有 15 列,所以迭代次数将是 397464*15 = 5961960 + 外循环 (397464) = 6359424迭代。

问题是这需要很长时间才能导致页面超时。 可以用更有效的方式编写吗?

var rowHtml = String.Empty;

foreach (DataRow row in queryResult.Rows)
{
    rowHtml += "<tr>";
    for (int i = 0; i < queryResult.Columns.Count; i++)
    {
        rowHtml += $"<td>{row[i]}</td>";
    }
    rowHtml += "</tr>";
}

【问题讨论】:

  • 您可以通过使用StringBuilder 而不是像这样连接字符串来显着减少执行时间和内存消耗。
  • 一个简单的想法是使用StringBuilder 创建您的html。它可能会向您显示较低的时间。此外,由于所有行都是独立的,您可能希望并行处理它们,例如使用Parallel.For
  • 您可能还想使用string.Join(但这可能证明效率低于字符串生成器)。
  • 你打算用这个 HTML 做什么?您肯定不会尝试同时向您的用户显示所有 397464 行吗?
  • "397464*15 = 5961960 + 外循环 (397464) = 6359424 次迭代。" - 你的意思是串联。然后是 5961960 + 2 * 外循环 (397464) ... 对于内循环体,您“仅”有 column*row = 15 * 397464 iterations

标签: c# performance for-loop foreach


【解决方案1】:

构建字符串:考虑使用StringBuilder。每次使用 + 运算符连接字符串时,都会在堆上创建一个新字符串。这对于个人用途来说很好,但在像您这样的大型工作负载中可能会大大减慢速度。您可以在构造函数中指定 StringBuilder 的最大容量和起始容量,从而更好地控制应用的内存使用情况。

并行化:我不知道您的应用的确切上下文,但我建议查看 System.Threading.Parallel 类。它的 For/Foreach 方法允许您使用线程池对集合进行迭代,这可以通过将其转移到多个内核来大大加快处理速度。

但请注意:如果元素的顺序相关,则应将工作负载划分为包,并为每个包构建子字符串。

编辑: 更正:字符串连接只能在极少数情况下真正并行化,其中循环产生的每个子字符串的确切长度是固定且已知的。在这种特殊情况下,可以将结果直接写入预先分配的大型目标缓冲区。这在使用 char 数组或指针时是完全可行的,但不建议使用普通的 C# 字符串或 StringBuilders。

异步处理:看起来您正在编写某种网络应用程序或服务器后端。如果您的内容是按需需要的,并且不需要在页面加载的确切时刻准备好,请考虑在页面等待您的服务器发送完成处理结果。

编辑: 正如 cmets 中所建议的,有比从表中构造 HTML 字符串更好的方法来解决这个问题。考虑使用那些而不是一些复杂的内容加载方案。

【讨论】:

  • 字符串生成器之后的所有内容都是错误的,实际上会降低性能。这个循环所做的就是连接字符串。没有异步操作或任何需要并行化的东西
  • @PanagiotisKanavos:我想问一下为什么并行化在这里不是一个好主意?该问题要求以更快或更有效的方式进行操作,而 Parallel.For 应该能够通过将其分成多个部分并同时处理这些部分来加快操作。我知道在某些情况下(尤其是在非常小的工作负载中) Parallel.For 只会产生不必要的开销,但这对我来说并不像那种情况。我可以请你指出我的错误吗?
  • 因为没有什么可并行化的。 only 操作是字符串连接,如果没有一些技巧就无法并行化 - 例如使用 AggregateStringBuilder 作为循环和最终状态。这会导致 lot 的缓冲区重新分配,因此必须提前指定容量。但这样做会阻止网络服务器服务其他请求
  • 此外,如果您检查 OP 尝试做什么,看起来真正的问题是如何在 Web 应用程序中将 300K 行导出到 Excel。正确地执行此操作会比并行化更快,并且对 Web 应用程序的影响更小。使用 DbDataReader 而不是 DataTable 也会更快,因为字符串连接比从数据库读取数据。整个操作可以在读取最后一行后立即完成
  • 你说得对,这确实是我这边的一个重大失误。唯一可行的并行化情况是,如果所有子字符串都具有已知的固定长度,并且我们改为写入 char 数组。我将编辑我的答案来解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多