OpenMp 不使用所有 CPU(双插槽、Windows 和 Microsoft Visual Studio)
Posted
技术标签:
【中文标题】OpenMp 不使用所有 CPU(双插槽、Windows 和 Microsoft Visual Studio)【英文标题】:OpenMp doesn't utilize all CPUs(dual socket, windows and Microsoft visual studio) 【发布时间】:2019-04-19 18:37:52 【问题描述】:我有一个双插槽系统,每个 CPU 有 22 个真实内核或每个 CPU 有 44 个超线程。我可以让 openMP 完全利用第一个 CPU(22 核/44 超),但我不能让它利用第二个 CPU。
我正在使用 CPUID HWMonitor 检查我的核心使用情况。第二个 CPU 在所有内核上始终处于或接近 0%。
使用:
int nProcessors = omp_get_max_threads();
得到我 nProcessors = 44,但我认为它只是使用 1 个 CPU 的 44 个超线程而不是 44 个真正的内核(应该是 88 个超线程)
看了很多遍,不知道如何利用其他CPU。
我的 CPU 运行良好,因为我可以运行使用所有 CPU 的其他并行处理程序。
我正在用 64 位编译它,但我认为这并不重要。另外,我使用的是 Visual Studio 2017 Professional 15.2 版。打开 MP 2.0(只有一个 vs 支持)。在 64 位 Windows 10 Pro 上运行,配备 2 个 Intel Xeon E5-2699v4 @ 2.2Ghz 处理器。
【问题讨论】:
提及您使用的操作系统以及您使用的编译器和 OpenMP 版本/实现可能会有所帮助。 您可以使用omp_set_num_threads
和omp_set_dynamic(false)
强制线程数禁用动态团队。我不确定它在 2-socket 设置上的表现如何。或许this read 能给出一点答案?
您可以设置线程数,它的表现很好,线程的分配方式取决于操作系统。操作系统可能会尝试将它们全部保留在同一个 CPU 上以确保更快的内存访问,我真的不知道 Windows 在多插槽计算机上的行为(或者哪些版本甚至支持它 - 我假设并非所有人都支持)。
@AlexG:我将 num_threads 设置为 44 和 88(以防万一它认为超线程是一个线程?) omp_set_dynamic 为 false,但它仍然没有使用其他 CPU。你提到的帖子是由于一个 linux 内核错误,这与 Qubit 可能提到的 Windows 如何处理事情有关。这就是为什么我不知道我还能做些什么来帮助解决问题。我现在应该将 44 个线程作为最大线程返回吗?还是应该是88?我不确定。
@Marvg 您可能想阅读this 和this。不确定 OMP_PLACES 出现在哪个版本中(我认为是 OpenMP 4.0),但它似乎与您的情况有关。
【参考方案1】:
感谢@AlexG 提供了一些见解来回答我自己的问题。请参阅问题的 cmets 部分。
这是 Microsoft Visual Studio 和 Windows 的问题。
先读Processor Groups for Windows。
基本上,如果您有 64 个以下的逻辑核心,这将不是问题。但是,一旦您克服了这一点,您现在将为每个套接字(或 Windows 这样选择的其他组织)拥有两个进程组。在我的例子中,每个进程组有 44 个超线程并代表一个物理 CPU 插槽,而我正好有两个进程组。默认情况下,每个进程(程序)只能访问一个进程组,因此我最初只能在一个内核上使用 44 个线程。但是,如果您手动创建线程并使用 SetThreadGroupAffinity 将线程的处理器组设置为与程序最初分配的组不同的组,那么您的程序现在将成为多处理器组。这似乎是启用多处理器的一种迂回方式,但是的,这就是如何做到这一点。一旦开始设置每个线程的单独进程组,调用 GetProcessGroupAffinity 将显示组数大于 1。
我能够像这样创建一个开放的 MP 块,并通过并分配进程组:
...
#pragma omp parallel num_threads( 88 )
HANDLE thread = GetCurrentThread();
if (omp_get_thread_num() > 32)
// Reserved has to be zero'd out after each use if reusing structure...
GroupAffinity1.Reserved[0] = 0;
GroupAffinity1.Reserved[1] = 0;
GroupAffinity1.Reserved[2] = 0;
GroupAffinity1.Group = 0;
GroupAffinity1.Mask = 1 << (omp_get_thread_num()%32);
if (SetThreadGroupAffinity(thread, &GroupAffinity1, &previousAffinity))
sprintf(buf, "Thread set to group 0: %d\n", omp_get_thread_num());
OutputDebugString(buf);
else
// Reserved has to be zero'd out after each use if reusing structure...
GroupAffinity2.Reserved[0] = 0;
GroupAffinity2.Reserved[1] = 0;
GroupAffinity2.Reserved[2] = 0;
GroupAffinity2.Group = 1;
GroupAffinity2.Mask = 1 << (omp_get_thread_num() % 32);
if (SetThreadGroupAffinity(thread, &GroupAffinity2, &previousAffinity))
sprintf(buf, "Thread set to group 1: %d\n", omp_get_thread_num());
OutputDebugString(buf);
所以使用上面的代码,我能够强制运行 64 个线程,每个套接字 32 个线程。现在,即使我尝试将 omp_set_num_threads 强制设置为 88,我也无法超过 64 个线程。原因似乎与 Visual Studio 的 OpenMP 实现不允许超过 64 个 OpenMP 线程有关。这是一个关于更多的链接information
感谢大家帮助收集更多有助于最终回答的花絮!
【讨论】:
很高兴你能找到一些东西。如果您真的需要性能,您可能还想在没有超线程的情况下进行一些测试。在最好的情况下,它会带来 15% 的性能提升,但如果你在处理数字,它可能会减慢你的速度。以上是关于OpenMp 不使用所有 CPU(双插槽、Windows 和 Microsoft Visual Studio)的主要内容,如果未能解决你的问题,请参考以下文章