多线程 C++ 代码速度越慢,物理内核越多? (线程 C++ mex 函数)
Multi-threaded C++ code slower with more physical cores? (threaded C++ mex function)
我现在 运行正在不同机器上运行多线程 C++ 代码。我在 Matlab mex 函数中使用它,所以整个程序是来自 MatLab 的 运行。我在这里使用了 this link 中的代码,只是更改了“main_loop”中的代码以适应我的任务。该代码在我的两台计算机上 运行ning 非常好,而且它比 运行ning 相同的 C++ 代码作为单线程快很多倍。所以我觉得程序本身是可以的。
但是,当我运行在第三台机器上做同样的事情时,突然变得非常慢。单线程版本很好,但多线程版本要长 10-15 倍。现在,由于在其他计算机上一切正常,我猜测它与第三台计算机的规格有关(详情见下文)。我的猜测:第三台计算机有两个物理处理器。我想这需要将所有内容物理复制到两个处理器? (原始代码是有意编写的,因此不需要任何涉及变量的硬拷贝)如果是这样,是否有办法控制在哪个处理器上打开线程? (如果我可以将自己限制在一个 CPU 并避免复制所有内容,那将会有所帮助)我已经尝试将线程数设置为 2,但没有帮助。
2-CPU 计算机的规格:
- Intel Xeon Silver 4210R,2.40Ghz(2 次),128 GB Ram 64 位,Windows
10 专业版
其他计算机的规格:
- 英特尔酷睿 i7-8700、3.2Ghz、64 GB 内存 64 位、Windows 10 Pro
- 英特尔酷睿 i7-10750H,2.6Ghz,16 GB Ram 64 位,Windows 10 Pro,笔记本电脑
TL;DR: NUMA 效果 结合 false-sharing 非常可能仅在 2 插槽系统上产生观察到的效果。 Low-level 分析信息 confirm/disprove 假设。
Multi-processors 系统受 NUMA 影响。 Non-uniform memory access 平台由具有自己的本地内存的 NUMA 节点组成。访问另一个节点的内存更昂贵(更高的延迟 or/and 更小的吞吐量)。位于访问同一 NUMA 节点内存的多个 NUMA 节点上的倍数 threads/processes 会使它饱和。
分配的内存分为 pages 映射到 NUMA 节点。确切的映射策略非常依赖于操作系统 (OS)、其配置和目标进程之一。 首次接触 政策很常见。这个想法是在对目标页面执行第一次访问的 NUMA 节点上分配页面。关于目标选择策略,OS 可以将页面从一个 NUMA 节点迁移到另一个关于远程 NUMA 节点访问量的页面。控制策略在 NUMA 平台上至关重要,尤其是当应用程序不是 NUMA-aware.
时
由于 cache coherence protocol 和 high-performance inter-processor 通信网络(在您的情况下为超路径互连),多个 NUMA 节点的内存保持一致。高速缓存一致性也适用于同一处理器的内核之间。问题是将 缓存行 从一个内核(的 L2 缓存)移动到另一个(L2 缓存)比将它从一个处理器(的 L3 缓存)移动到另一个处理器快得多( L3缓存)。这是人类交流的类比:不同皮层区域的神经元交流比两个人在一起交流更快。
如果您的应用程序在同一缓存行上并行运行,false-sharing 可能会导致 cache-line 弹跳 效果在分布在不同处理器上的线程之间更为明显。
这是一个非常复杂的话题。也就是说,您可以使用像 VTune(或 Linux 上的 perf
)这样的 low-level profilers 来分析这些影响。这个想法是分析 low-level 性能硬件计数器,如 L2/L3 缓存 misses/hit、RFO、远程 NUMA 访问等。对于不熟悉处理器和OS 有效,但 VTune 有所帮助。请注意,英特尔有一些更具体的工具可以(更容易地)分析通常发生在并行应用程序上的这种特定影响。 AFAIK,它们是 Intel XE 应用程序集的一部分(不是免费的)。最好的办法是避免false-sharing使用填充,设计你的应用程序,这样每个线程应该尽可能多地在自己的内存位置上运行一个可能的(即良好的位置),控制 NUMA 分配策略,最后 绑定 threads/processes 到核心 (以避免意外迁移)。
实验基准也可用于快速检查是否发生 NUMA 效应和错误共享。例如,您可以将所有 threads/processes 绑定在同一个 NUMA 节点上,并告诉 OS 在这个 NUMA 节点上分配页面。这使您能够找到与 NUMA 效果相关的问题。另一个例子是在相同物理内核的两个不同逻辑内核(即硬件线程)上绑定两个threads/processes,然后在不同的物理内核上查看是否影响性能。这个可以帮助您定位虚假共享问题。话虽如此,此类实验可能会受到许多其他影响的影响,这些影响会增加噪音并使大型应用程序的分析在实践中变得相当复杂。因此,low-level 基于硬件性能计数器的分析更好。
请注意,某些处理器(如 AMD Zen)由多个 sub-parts(称为 CCD/CCX)组成,即使只有一个处理器和一个插槽,也可以看到具有多个 NUMA 节点。这样的架构在未来肯定会变得更加普遍。事实上,英特尔也开始往这个方向发展。
我现在 运行正在不同机器上运行多线程 C++ 代码。我在 Matlab mex 函数中使用它,所以整个程序是来自 MatLab 的 运行。我在这里使用了 this link 中的代码,只是更改了“main_loop”中的代码以适应我的任务。该代码在我的两台计算机上 运行ning 非常好,而且它比 运行ning 相同的 C++ 代码作为单线程快很多倍。所以我觉得程序本身是可以的。
但是,当我运行在第三台机器上做同样的事情时,突然变得非常慢。单线程版本很好,但多线程版本要长 10-15 倍。现在,由于在其他计算机上一切正常,我猜测它与第三台计算机的规格有关(详情见下文)。我的猜测:第三台计算机有两个物理处理器。我想这需要将所有内容物理复制到两个处理器? (原始代码是有意编写的,因此不需要任何涉及变量的硬拷贝)如果是这样,是否有办法控制在哪个处理器上打开线程? (如果我可以将自己限制在一个 CPU 并避免复制所有内容,那将会有所帮助)我已经尝试将线程数设置为 2,但没有帮助。
2-CPU 计算机的规格:
- Intel Xeon Silver 4210R,2.40Ghz(2 次),128 GB Ram 64 位,Windows 10 专业版
其他计算机的规格:
- 英特尔酷睿 i7-8700、3.2Ghz、64 GB 内存 64 位、Windows 10 Pro
- 英特尔酷睿 i7-10750H,2.6Ghz,16 GB Ram 64 位,Windows 10 Pro,笔记本电脑
TL;DR: NUMA 效果 结合 false-sharing 非常可能仅在 2 插槽系统上产生观察到的效果。 Low-level 分析信息 confirm/disprove 假设。
Multi-processors 系统受 NUMA 影响。 Non-uniform memory access 平台由具有自己的本地内存的 NUMA 节点组成。访问另一个节点的内存更昂贵(更高的延迟 or/and 更小的吞吐量)。位于访问同一 NUMA 节点内存的多个 NUMA 节点上的倍数 threads/processes 会使它饱和。
分配的内存分为 pages 映射到 NUMA 节点。确切的映射策略非常依赖于操作系统 (OS)、其配置和目标进程之一。 首次接触 政策很常见。这个想法是在对目标页面执行第一次访问的 NUMA 节点上分配页面。关于目标选择策略,OS 可以将页面从一个 NUMA 节点迁移到另一个关于远程 NUMA 节点访问量的页面。控制策略在 NUMA 平台上至关重要,尤其是当应用程序不是 NUMA-aware.
时由于 cache coherence protocol 和 high-performance inter-processor 通信网络(在您的情况下为超路径互连),多个 NUMA 节点的内存保持一致。高速缓存一致性也适用于同一处理器的内核之间。问题是将 缓存行 从一个内核(的 L2 缓存)移动到另一个(L2 缓存)比将它从一个处理器(的 L3 缓存)移动到另一个处理器快得多( L3缓存)。这是人类交流的类比:不同皮层区域的神经元交流比两个人在一起交流更快。
如果您的应用程序在同一缓存行上并行运行,false-sharing 可能会导致 cache-line 弹跳 效果在分布在不同处理器上的线程之间更为明显。
这是一个非常复杂的话题。也就是说,您可以使用像 VTune(或 Linux 上的 perf
)这样的 low-level profilers 来分析这些影响。这个想法是分析 low-level 性能硬件计数器,如 L2/L3 缓存 misses/hit、RFO、远程 NUMA 访问等。对于不熟悉处理器和OS 有效,但 VTune 有所帮助。请注意,英特尔有一些更具体的工具可以(更容易地)分析通常发生在并行应用程序上的这种特定影响。 AFAIK,它们是 Intel XE 应用程序集的一部分(不是免费的)。最好的办法是避免false-sharing使用填充,设计你的应用程序,这样每个线程应该尽可能多地在自己的内存位置上运行一个可能的(即良好的位置),控制 NUMA 分配策略,最后 绑定 threads/processes 到核心 (以避免意外迁移)。
实验基准也可用于快速检查是否发生 NUMA 效应和错误共享。例如,您可以将所有 threads/processes 绑定在同一个 NUMA 节点上,并告诉 OS 在这个 NUMA 节点上分配页面。这使您能够找到与 NUMA 效果相关的问题。另一个例子是在相同物理内核的两个不同逻辑内核(即硬件线程)上绑定两个threads/processes,然后在不同的物理内核上查看是否影响性能。这个可以帮助您定位虚假共享问题。话虽如此,此类实验可能会受到许多其他影响的影响,这些影响会增加噪音并使大型应用程序的分析在实践中变得相当复杂。因此,low-level 基于硬件性能计数器的分析更好。
请注意,某些处理器(如 AMD Zen)由多个 sub-parts(称为 CCD/CCX)组成,即使只有一个处理器和一个插槽,也可以看到具有多个 NUMA 节点。这样的架构在未来肯定会变得更加普遍。事实上,英特尔也开始往这个方向发展