如何检查内存带宽是否已成为瓶颈?

时间:2013-10-08 02:46:22

标签: c multithreading performance memory

我正在开发一个高度并发的C程序,当核心数小于8时,它可以很好地扩展,但拒绝扩展到超过8个核心。

我怀疑内存带宽是瓶颈,我该如何验证是否属实?

是否有任何可以帮助诊断的工具/技术/操作系统功能?

3 个答案:

答案 0 :(得分:12)

我自己在NUMA 96x8核心机上遇到过这个问题。

90%的时间是内存/缓存同步问题。如果经常调用同步例程(原子,互斥),则必须在所有套接字上使相应的高速缓存行无效,从而导致整个内存总线完全锁定多个周期。

您可以通过运行Intel VTunePerfsuite等分析器对其进行分析,并记录您的原子需要多长时间。如果你正确使用它们,那么它们应该采取10-40个周期之间的东西。最糟糕的情况是,在将多线程应用程序扩展到8个套接字(Intel Xeon上的8x8内核)时,我有300个周期。

你可以做的另一个简单的分析步骤是在没有任何原子/互斥体的情况下进行编译(如果你的代码允许)并在多个套接字上运行它 - 它应该运行得很快(不正确但很快)。

您的代码在8个内核上快速运行的原因是因为英特尔处理器在执行原子时使用缓存锁定,只要您将所有代码保存在同一物理芯片(套接字)上即可。如果一个锁必须进入内存总线 - 这就是事情变得丑陋的时候。

我唯一可以建议的是:缩小您调用原子/同步例程的频率。

至于我的应用程序:我必须实现一个几乎无锁的数据结构,以便将我的代码扩展到一个套接字之外。每个线程都会累积需要锁定的操作,并定期检查它是否可以刷新它们。然后传递令牌并轮流刷新同步操作。显然,只有在等待时有足够的工作要做才有效。

答案 1 :(得分:2)

+1好问题。

首先,我想说还有其他因素需要考虑,例如:缓存同步,或不可避免的序列化部分,如原子内存操作,这也是可能的瓶颈,比内存带宽更容易验证。

至于内存带宽,我现在拥有的是一个天真的想法,即通过简单地重复访问主内存来启动一个简单的守护进程来消耗内存带宽,同时简单地重复访问主内存(一定要考虑存在高速缓存)。使用守护程序,您可以调整并记录它消耗的内存带宽,并将此结果与应用程序的性能进行比较。

很抱歉提供这样一个草率的答案..虽然它是可行的XD

已编辑:另请参阅How to measure memory bandwidth currently being used on Linux?How can I observe memory bandwidth?

答案 2 :(得分:1)

虽然获得有关算法和平台的更多信息会很有帮助,但通常有一些原因导致应用程序无法扩展:

  1. 使用显式同步(互斥/原子/事务等):并行程序中的同步意味着您必须在多个线程之间共享资源时创建一些顺序部分。想要访问关键部分的线程越多(原子操作实际上是一个非常小的关键部分),您拥有的争用越多,可扩展性就越有限,因为核心正在轮流进入关键部分。减少关键部分的大小并选择不同的数据结构/算法可以减少资源私有化的可能性。

  2. 虚假共享:两个或多个线程共享不相关的对象,这些对象恰好在同一个缓存块中。当您将应用程序从一个核心扩展到更多,从一个套接字扩展到更多一个套接字时,通常可以通过查看增加的缓存未命中来轻松检测。将数据结构与缓存块大小对齐通常可以解决这个问题。另请参阅Eliminate False Sharing - Dr Dobb's

  3. 内存分配/释放:虽然内存分配会为您提供不同的内存块,但您可能会在分配或甚至解除分配时发生争用。可以通过使用可扩展的线程安全内存分配器来解决,例如Intel TBB's scalable allocatorHoard等。

  4. 空闲线程:您的算法是否具有生产者/消费者模式,是否可能比您生产的消耗更快?您的数据大小是否足够大,以便您分摊并行化的成本,并且您不会因失去地方而失去速度?由于任何其他原因,您的算法本质上是不可扩展的吗?您可能需要告诉我们有关您的平台和算法的更多信息。 Intel Advisor是检查并行化的最佳方式的一个不错的工具。

  5. 并行框架:你在用什么? OpenMP,英特尔TBB,还有什么?纯线程?您是否可能分叉/加入太多或过度分配您的问题?您的运行时本身是否可扩展?

  6. 其他技术原因:线程与核心的绑定不正确(可能多个线程最终在同一个核心上),并行运行时的特性(英特尔的OpenMP运行时有一个额外的隐藏线程,执行线程到核心绑定可以映射这个额外的线程与主线程在同一个核心上,毁了你的一天)等。

  7. 根据我的经验,我发现一旦消除了上述所有内容,就可以开始怀疑内存带宽。您可以使用STREAM轻松检查它,它可以告诉您内存带宽是否是限制因素。英特尔网站上有article解释了如何检测内存带宽饱和度。

    如果以上都不是最终结论,那么实际上协调协议流量和/或NUMA(非统一内存访问,acmqueue中的一篇好文章)可能实际上具有有限的可扩展性。每当您访问内存中的某个对象时,您要么生成缓存失效请求(您正在共享某些内容并且缓存一致性协议启动),要么您正在访问位于靠近另一个套接字的存储区中的内存(您正在通过处理器互连) )。