【问题标题】:Multithreaded resource contention多线程资源争用
【发布时间】:2011-12-11 11:52:24
【问题描述】:

我正在分析一个使用不同数量的允许线程运行的多线程程序。以下是相同输入工作的 3 次运行的性能结果。

1 thread:
  Total thread time: 60 minutes.
  Total wall clock time: 60 minutes.

10 threads:
  Total thread time: 80 minutes. (Worked 33% longer)
  Total wall clock time: 18 minutes.  3.3 times speed up

20 threads
  Total thread time: 120 minutes. (Worked 100% longer)
  Total wall clock time: 12 minutes.  5 times speed up

由于做同样的工作需要更多的线程时间,我觉得线程一定在争夺资源。

我已经检查了应用程序机器和数据库服务器上的四大支柱(cpu、内存、diskIO、网络)。内存是最初的竞争资源,但现在已经修复(始终超过 1G 可用)。在 20 线程测试中,CPU 徘徊在 30% 到 70% 之间,所以很多。 diskIO 在应用程序机器上几乎没有,在数据库服务器上几乎没有。网络真的很棒。

我还使用 redgate 进行了代码分析,发现没有等待锁的方法。它有助于线程不共享实例。现在我正在检查更细微的项目,例如数据库连接建立/池(如果 20 个线程尝试连接到同一个数据库,它们是否必须相互等待?)。

我正在尝试识别和解决资源争用问题,以便 20 线程运行如下所示:

20 threads
  Total thread time: 60 minutes. (Worked 0% longer)
  Total wall clock time: 6 minutes.  10 times speed up

我应该查看哪些最有可能的来源(除了四大来源)以找到这种争用?


每个线程执行的代码大概是:

Run ~50 compiled LinqToSql queries
Run ILOG Rules
Call WCF Service which runs ~50 compiled LinqToSql queries, returns some data
Run more ILOG Rules
Call another WCF service which uses devexpress to render a pdf, returns as binary data
Store pdf to network
Use LinqToSql to update/insert. DTC is involved: multiple databases, one server.

WCF 服务在同一台机器上运行,并且是无状态的,能够同时处理多个请求。


机器有 8 个 CPU。

【问题讨论】:

  • 建议您发布您尝试并行化的代码/算法
  • 你的机器有多少个 CPU?
  • 你的操作大多是IO绑定的,这些IO调用是异步的吗?如果不尝试让它们异步,然后看看你是否有任何好处
  • 好吧,我很感激这种能帮助我“解决”性能问题的本能。在这个阶段,衡量问题先于解决方案。我要求测量更多的东西。
  • 在此过程中进行一些堆栈转储,您将自己看到争用。

标签: .net multithreading profiling contention


【解决方案1】:

是的,存在资源争用。例如,所有线程都必须将数据读/写到相同的内存总线,定向到相同的 RAM 模块。有多少可用 RAM 无关紧要,重要的是读/写由相同 RAM 模块上的相同内存控制器执行,并且数据通过相同总线传输。

如果有任何同步任何地方,那么这也是一个竞争资源。如果有任何 I/O,那就是竞争资源。

从 1 个线程到 N 个线程时,您永远不会看到 N x 加速。这是不可能的,因为最终,CPU 中的所有东西都是共享资源,会存在一定程度的争用。

有很多因素会阻止您获得完整的线性加速。您假设数据库、运行数据库的服务器、将其连接到客户端的网络、客户端计算机、两端的操作系统和驱动程序、内存子系统、磁盘 I/O 和一切 当你从 1 到 20 个线程时,它们之间的速度只会快 20 倍。

两个字:梦想。

这些瓶颈中的每一个只需让您慢几个百分点,然后总体结果就会像您所看到的那样。

我相信你可以调整它以更好地扩展,但不要指望奇迹。

但您可能会寻找的一件事是缓存行共享。线程是否访问与其他线程使用的数据非常接近的数据?您多久可以避免这种情况发生?

【讨论】:

  • 我目前处于 N/4 加速,正在寻找 N/2 加速。线程被交给一个带有一批标识符的请求对象 - 从那里他们使用这些标识符来加载他们自己的数据以使用。
  • 您关于 DB IO、DB CPU、DB 内存、网络容量、网络 ping、客户端 CPU、客户端内存、客户端磁盘 IO 快 20 倍的观点:已经检查过这些东西 - 它们不在容量。我不希望它们快 20 倍,我希望通过不断地使用它们来使用它们中的更多,而不是轮流使用它们。他们只是坐在那里低于容量。其他东西已经满负荷,这就是我正在寻找的东西。
【解决方案2】:

不是测量总线程时间,而是测量执行某种 I/O(数据库、磁盘、网络等)的每个操作的时间。

我怀疑您会发现,当您拥有更多线程时,这些操作会花费更长的时间,这是因为争用发生在该 I/O 的另一端。例如,您的数据库可能正在对数据一致性请求进行序列化。

【讨论】:

  • +1 用于数据库锁争用。我希望我知道一种衡量它的好方法。
  • 您可以运行单线程测试并测量所有单独的数据库操作。然后将它们加在一起,假设在 60 分钟中总共有 5 分钟用于数据库访问。现在您知道,无论您的流程有多高效,您将永远拥有 5 分钟。因此,您应该将其视为 M+N 类型的事情,M 是处理时间,N 是访问您无法控制的共享资源所花费的时间。您可以通过多线程来改进 M,但对于 N,您无能为力,它始终是固定的。
【解决方案3】:

您所描述的是,您想要 100% 的可伸缩性,即线程 s 的增加和 wallcklock 时间的减少之间的 1:1 关系......这通常是一个目标,但很难达到......

例如,您写道没有内存争用,因为有 1 GB 空闲空间......恕我直言,这是一个错误的假设......内存争用还意味着如果两个线程尝试分配内存,则可能会发生一个必须等待另一个...要记住的另一个要点是 GC 发生的中断,它会暂时冻结所有线程...可以通过配置(gcServer)对 GC 进行一些自定义 - 请参阅http://blogs.msdn.com/b/clyon/archive/2004/09/08/226981.aspx

另一点是 WCF 服务调用...如果它不能按比例放大 - 例如 PDF 渲染 - 那么这也是一种竞争形式例如...

可能的争论是“无穷无尽的”......而且几乎总是在你提到的明显领域......

编辑 - 根据 cmets:

需要检查的几点:

  • 连接池
    您使用什么提供商?它是如何配置的?
  • PDF 渲染
    可能的争用将在您使用的库内的某处测量...
  • Linq2SQL
    检查所有这些查询的执行计划...可能有一些采用任何类型的锁定,因此可能会创建争用 DB-server-side...

编辑 2:

话题
这些线程来自 ThreadPool 吗?如果是这样,那么您将无法扩展:-(

编辑 3:

ThreadPool 线程不适合长时间运行的任务,您的场景就是这种情况...有关详细信息,请参阅

来自http://www.yoda.arachsys.com/csharp/threads/printable.shtml

长时间运行的操作应该使用新创建的线程; 短期运行的操作可以利用线程池。

如果您想要极致性能,那么值得查看CQRS 和描述为LMAX 的真实示例。

【讨论】:

  • 只追求 50% 的加速(20 个线程,10 倍加速)。我很幸运,PDF 渲染很快就会从流程中删除。尽管如此,如果线程正在等待那个 pdf 渲染,那么应该有一些可测量的资源,我可以查看它来确定它,对吗?我肯定会找到一些方法来测量 redgate 中的 GC。我不是要无休止的清单,只是“接下来要检查的几项”清单。
  • 要测量有关 PDF 渲染的资源,您必须检查用于渲染的库中的某些内容...另一点是如何处理您的数据库连接/连接池 - 您使用什么提供程序/配置采用 ?还要检查所有使用的 Linq2SQL 的执行计划(你可以使用一些 DB-server-side 工具)
  • 如果库被锁定,我只需要查看库内部,对吗?如果它是一个内存猪,我就不必在库中查看 - 我会查看机器上的指标。我使用默认的连接池——当 LinqToSql 的 DataContext 管理连接时会发生什么。我生活和呼吸执行计划 - 这已经完成了。
  • 一些 PDF 库在技术上是这样编写的,因此它们只能在一个线程上运行 - 我不知道 devexpress...但我已经尝试了很多 PDF 库并且不会打赌它总是一个锁......因为我从不将 Linq2SQL 用于性能敏感的事情我不能告诉你很多关于在这种情况下的连接池......你使用 SQL Server 或 Oracle 还是?您使用的线程是来自 ThreadPool 吗?
  • ThreadPool 线程不应该用于长时间运行的任务(就像在这种情况下)...查看我的编辑和那里的链接...
猜你喜欢
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-27
相关资源
最近更新 更多