Parallel processing 多核CPU';s:避免令人失望的可伸缩性的编程技术

Parallel processing 多核CPU';s:避免令人失望的可伸缩性的编程技术,parallel-processing,cpu,multicore,numa,Parallel Processing,Cpu,Multicore,Numa,我们刚买了一台32核的Opteron机器,我们得到的加速效果有点令人失望:超过24个线程,我们根本看不到加速效果(实际上整体速度变慢了),在大约6个线程之后,它变得明显地亚线性 我们的应用程序非常线程友好:我们的工作分解为大约170000个小任务,每个任务可以单独执行,每个任务需要5-10秒。它们都从相同的内存映射文件中读取,大小约为4Gb。他们偶尔会对其进行写入,但每次写入可能有10000次读取-我们只是在170000个任务中的每个任务结束时写入一点数据。写操作是锁保护的。分析表明锁不是问题。

我们刚买了一台32核的Opteron机器,我们得到的加速效果有点令人失望:超过24个线程,我们根本看不到加速效果(实际上整体速度变慢了),在大约6个线程之后,它变得明显地亚线性

我们的应用程序非常线程友好:我们的工作分解为大约170000个小任务,每个任务可以单独执行,每个任务需要5-10秒。它们都从相同的内存映射文件中读取,大小约为4Gb。他们偶尔会对其进行写入,但每次写入可能有10000次读取-我们只是在170000个任务中的每个任务结束时写入一点数据。写操作是锁保护的。分析表明锁不是问题。线程在非共享对象中使用大量JVM内存,它们对共享JVM对象的访问非常少,其中只有一小部分访问涉及写操作

我们在Linux上用Java编程,并启用NUMA。我们有128Gb内存。我们有2台Opteron CPU(6274型),每台16核。每个CPU有2个NUMA节点。在英特尔四核(即8核)上运行的相同作业几乎线性扩展到8个线程

我们尝试复制只读数据,使每个线程都有一个,希望大多数查找可以是NUMA节点的本地查找,但我们没有观察到任何加速

对于32个线程,“top”显示CPU的74%的“us”(用户)和23%的“id”(空闲)。但是没有休眠,几乎没有磁盘i/o。使用24个线程,我们可以获得83%的CPU使用率。我不知道如何解释“空闲”状态-这是否意味着“等待内存控制器”

我们尝试打开和关闭NUMA(我指的是需要重新启动的Linux级别设置),但没有发现任何区别。启用NUMA时,“numastat”仅显示约5%的“分配和访问未命中”(95%的缓存未命中是NUMA节点本地的)。[编辑:]但是添加“-XX:+useNUMA”作为java命令行标志给了我们10%的提升

我们的一个理论是,我们正在最大限度地使用内存控制器,因为我们的应用程序使用了大量RAM,并且我们认为存在大量缓存未命中

我们可以做些什么来(a)加速我们的程序以接近线性可伸缩性,或者(b)诊断发生了什么?


另外:(c)我如何解释“top”结果——“idle”是否表示“内存控制器阻塞”?(d)Opteron和Xeon的特性有什么不同吗?

我假设您已经优化了锁,并且使同步最小化。在这种情况下,它仍然在很大程度上取决于用于并行编程的库

即使没有同步问题,也可能发生的一个问题是内存总线拥塞。这是非常令人讨厌和难以摆脱的。 我所能建议的就是以某种方式让你的任务更大,创建更少的任务。这在很大程度上取决于问题的性质。理想情况下,您需要的任务数量与内核/线程数量相同,但这并不容易(如果可能的话)实现

另一个有帮助的方法是为JVM提供更多堆。这将减少频繁运行垃圾收集器的需要,并稍微加快运行速度

“空闲”是否表示“内存控制器上被阻止”


不,你在上面看不到。我的意思是,如果CPU正在等待内存访问,它将显示为忙碌。如果有空闲时间,它要么等待锁定,要么等待IO。

我还有一台32核Opteron机器,有8个NUMA节点(4x6128处理器,Mangy Cours,而不是推土机),我也遇到过类似的问题

我想你的问题的答案可以从上面显示的2.3%的“sys”时间中得到暗示。根据我的经验,这个sys时间是系统在内核中等待锁的时间。当线程无法获得锁时,它将处于空闲状态,直到下一次尝试。sys和空闲时间都是锁争用的直接结果。您说您的分析器没有显示锁是问题所在。我的猜测是,出于某种原因,导致有问题的锁的代码没有包含在概要文件结果中

在我的例子中,锁争用的一个重要原因不是我实际正在做的处理,而是向每个线程分发单个工作的工作调度程序。这段代码使用锁来跟踪哪个线程正在执行哪个工作。我对这个问题的解决方案是重写我的工作调度程序,避免互斥,我已经读到互斥不能扩展到8-12核,而是使用gcc内置原子(我在Linux上用C编程)。原子操作实际上是一个非常细粒度的锁,它可以更好地扩展高内核数。在你的情况下,如果你的工作包裹真的需要5-10秒,这似乎不太可能对你有意义

我还遇到了malloc的问题,它在高内核数的情况下会遇到可怕的锁问题,但我记不清这是否会导致顶部出现sys&idle数字,或者它是否只是使用Mike Dunlavey的调试器评测方法()出现的。我怀疑它确实导致了sys&idle问题,但我在翻阅我所有的旧笔记以找出答案时划出了界限:)我确实知道我现在尽可能避免运行时malloc


我最好的猜测是,您正在使用的某些库代码在您不知情的情况下实现了锁,没有包含在您的分析结果中,并且没有很好地扩展到高内核数的情况。当心内存分配器

我相信答案将取决于对硬件架构的考虑。你必须把多核计算机想象成通过网络连接起来的独立机器。事实上,这就是Hypertransport和QPI的全部功能

我发现,要解决这些可伸缩性问题,您必须停止考虑共享内存,开始采用通信顺序进程的理念。这意味着思维方式完全不同