我正在开发一个高度并发的 C 语言程序,当核心数少于8时,它可以很好地扩展,但在超过8个核心时无法扩展。
我怀疑内存带宽是瓶颈,如何验证这一点?
是否有任何工具/技术/操作系统功能可以帮助诊断?
我正在开发一个高度并发的 C 语言程序,当核心数少于8时,它可以很好地扩展,但在超过8个核心时无法扩展。
我怀疑内存带宽是瓶颈,如何验证这一点?
是否有任何工具/技术/操作系统功能可以帮助诊断?
我在一台NUMA 96x8核心的机器上也遇到过这个问题。
90%的情况是由于内存/缓存同步出现了问题。如果您频繁调用同步程序(原子操作、互斥量),则所有套接字上的适当缓存行都必须无效化,导致整个内存总线被完全锁定多个周期。
您可以通过运行像Intel VTune或Perfsuite这样的性能分析器来分析此问题,并记录您的原子操作所需的时间。如果使用正确,则应该需要大约10-40个周期。最坏的情况是,在将我的多线程应用程序扩展到8个套接字(Intel Xeon上的8x8核心)时,我的原子操作需要300个周期。
另一个简单的分析步骤是,如果您的代码允许,请在没有任何原子/互斥量的情况下进行编译,然后在多个套接字上运行它-它应该运行得很快(不正确,但快速)。
您的代码之所以在8个核心上运行得很快,是因为英特尔处理器在执行原子操作时使用缓存锁定,只要您将它们全部保持在同一物理芯片(套接字)上。如果锁定必须转到内存总线,则情况就会变得糟糕。
我唯一能建议的是:减少调用原子操作/同步程序的频率。
至于我的应用程序:我不得不实现几乎无锁数据结构,以便将代码扩展到一个以上的套接字。每个线程积累需要锁定的操作,并定期检查是否轮到他刷新它们。然后传递令牌,轮流刷新同步操作。显然,只有在等待时有足够的工作可做时才有效。
+1 个好问题。
首先,我想说还有其他因素需要考虑,例如缓存同步或不可避免的序列化部分,如原子内存操作,这些也可能成为瓶颈,比内存带宽更容易验证。
至于内存带宽,我现在拥有的是一个天真的想法,就是启动一个简单的守护进程来消耗内存带宽,同时对您的应用程序进行分析,通过简单地重复访问主内存(请务必考虑缓存的存在)。使用该守护进程,您可以调整和记录其所消耗的内存带宽,并将此结果与您的应用程序性能进行比较。
很抱歉提供了如此粗糙的答案...虽然这是可行的 XD
编辑:另请参见如何测量当前在 Linux 上使用的内存带宽? 和 如何观察内存带宽?