【问题标题】:many-core CPU's: Programming techniques to avoid disappointing scalability多核 CPU:避免令人失望的可扩展性的编程技术
【发布时间】:2012-11-08 01:27:55
【问题描述】:

我们刚买了一台 32 核的 Opteron 机器,我们得到的加速有点令人失望:超过 24 个线程,我们根本看不到加速(实际上总体上变慢了),大约 6 个线程后它变得明显低于-线性。

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

我们在启用了 NUMA 的 Linux 上使用 Java 进行编程。我们有 128Gb 内存。我们有 2 个 Opteron CPU(型号 6274),每个 CPU 有 16 个内核。每个 CPU 有 2 个 NUMA 节点。在 Intel 四核(即 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”是否意味着“blocked on memory controllers”? (d) Opteron 与 Xeon 的特性有什么不同?

【问题讨论】:

  • 没有任何适用于 Java 的硬件级分析器吗?如果您的代码是用 C、C++、Fortran 或其他编译语言编写的,我会向您指出 likwidPAPI 之类的东西,以便获得硬件计数器读数。请注意,与至强不同,Bulldozer 的每对内核共享大量资源——指令解码器、L1 指令缓存和 L2 缓存、FP 调度程序、2 个 FMAC/AVX 引擎等。那么每个 Bulldozer 处理器本身也是一个 NUMA 设备——它基本上是一个包中的两个处理器,它们之间有一个 HT 链接。

标签: parallel-processing cpu multicore numa


【解决方案1】:

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

我认为顶部显示的 2.3%“sys”时间暗示了您问题的答案。根据我的经验,这个 sys 时间是系统在内核中等待锁定的时间。当一个线程无法获得锁时,它就会处于空闲状态,直到它进行下一次尝试。 sys 时间和空闲时间都是锁争用的直接结果。你说你的分析器没有显示锁是问题所在。我的猜测是,由于某种原因,导致锁定的代码未包含在配置文件结果中。

在我的情况下,锁争用的一个重要原因不是我实际执行的处理,而是将单个工作分配给每个线程的工作调度程序。此代码使用锁来跟踪哪个线程正在执行哪项工作。我对这个问题的解决方案是重写我的工作调度程序,避免使用互斥锁,我读过的互斥锁不能很好地扩展到 8-12 个内核之外,而是使用 gcc 内置原子(我在 Linux 上用 C 语言编程)。原子操作实际上是一种非常细粒度的锁,可以在高核心数的情况下更好地扩展。在您的情况下,如果您的工作包确实需要 5-10 秒,那么这对您来说似乎不太可能。

我也遇到了 malloc 的问题,它在高核心数的情况下会遇到可怕的锁定问题,但是我不知道这是否也导致了顶部的 sys 和空闲数字,或者它是否刚刚使用 Mike Dunlavey 的调试器分析方法 (How can I profile C++ code running in Linux?) 出现。我怀疑它确实会导致系统和空闲问题,但我在挖掘所有旧笔记以找出答案时划清界限:) 我知道我现在尽可能避免运行时 malloc。

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

【讨论】:

  • 感谢您的好回答。最初我非常确信锁与问题无关,但最近我对明显错误的分析类型信息感到沮丧,例如一个探查器不知道它在 100 行方法中的位置。所以你可能是对的。
【解决方案2】:

我确信答案在于对硬件架构的考虑。您必须将多核计算机视为通过网络连接的单个机器。事实上,这就是 Hypertransport 和 QPI 的全部内容。

我发现要解决这些可伸缩性问题,您必须停止考虑共享内存,并开始采用通信顺序进程的理念。这意味着思维方式非常不同,即想象一下,如果您的硬件是通过网络连接的 32 台单核机器,您将如何编写软件。现代(和古老)的 CPU 架构并非旨在提供您所追求的那种不受限制的扩展。它们旨在允许许多不同的进程继续处理自己的数据。

就像计算中的其他所有事物一样,这些事物都在流行。 CSP 可以追溯到 1970 年代,但非常现代的 Java 派生的 Scala 是该概念的流行体现。请参阅 Wikipedia 上有关 Scala 并发的 this 部分。

CSP 的理念是迫使您设计适合您的数据和您正在解决的问题的数据分发方案。这并不一定容易,但如果你管理它,那么你就有一个确实可以很好扩展的解决方案。 Scala 可能会让开发变得更容易。

就我个人而言,我用 CSP 和 C 语言做所有事情。它使我能够开发一个信号处理应用程序,该应用程序可以从 8 个内核完美线性扩展到几千个内核(限制是我的房间有多大)。

您要做的第一件事就是实际使用 NUMA。你打开它并不是一个神奇的设置,你必须在你的软件架构中利用它。我不了解 Java,但在 C 中,将内存分配绑定到特定内核的内存控制器(也称为内存关联),并且在操作系统没有得到提示的情况下,线程也类似(核心关联)。

我认为您的数据不会分解为 32 个整齐的离散块?如果不确切了解程序中隐含的数据流,就很难给出建议。但是从数据流的角度考虑。把它画出来;数据流图对此很有用(另一种古老的图形形式符号)。如果您的图片显示您的所有数据都通过单个对象(例如通过单个内存缓冲区),那么它会很慢......

【讨论】:

  • 感谢您的好回答。我们正在用 Java 编程,我们似乎对数据的放置几乎没有控制权,尽管我们可能可以控制线程亲和性。无奈之下,我们尝试复制大量只读数据,希望页面能够迁移到(或至少最初放置在)不同内核的 NUMA 节点。但是速度没有明显的提高。
【解决方案3】:

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

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

其他可以提供帮助的方法是为您的 JVM 提供更多堆。这将减少频繁运行垃圾收集器的需要,并加快一点速度。

“空闲”是否意味着“在内存控制器上阻塞”

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

【讨论】:

    【解决方案4】:

    我是原始海报。我们认为我们已经诊断出了问题,不是锁,不是系统调用,也不是内存总线拥塞;我们认为这是 2/3 级 CPU 缓存争用

    重申一下,我们的任务是令人尴尬的并行,因此它应该可以很好地扩展。但是,一个线程可以访问大量的 CPU 缓存,但是随着我们添加更多线程,每个进程可以访问的 CPU 缓存量越来越低(相同数量的缓存除以更多进程)。某些架构上的某些级别在裸片上的内核之间共享,有些甚至在裸片之间共享(我认为),这可能有助于使用您正在使用的特定机器“摆脱困境”,并优化您的算法,但我们的结论是,要实现我们认为可以实现的可扩展性,我们无能为力。

    我们使用 2 种不同的算法确定这是原因。访问更多 2/3 级缓存的缓存比使用更少数据进行更多处理的缓存更差。它们都频繁访问主存中的主要数据。

    【讨论】:

    • 也许算法有一些东西使它不可行——但是高并行化、读取大量数据并且只在最后写入,以及对缓存争用的担忧听起来像是 GPGPU/OpenCL/HSA堆栈可以更好地解决。
    【解决方案5】:

    如果您还没有尝试过:查看像 Oracle Studio 那样的硬件级别分析器(用于 CentOS、Redhat 和 Oracle Linux),或者如果您被 Windows 卡住了:英特尔 VTune。然后开始查看每个指令指标具有可疑高时钟的操作。可疑的高意味着比单 numa、单 L3 缓存机器(如当前的 Intel 桌面 CPU)上的相同代码高很多。

    【讨论】:

      猜你喜欢
      • 2023-03-14
      • 1970-01-01
      • 2010-11-29
      • 1970-01-01
      • 2013-11-29
      • 1970-01-01
      • 2017-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多