首页 > 解决方案 > OpenMP 使许多内核处于空闲状态

问题描述

我正在尝试使用 OMP 来并行化一些代码,并且在我的常规工作站上,我让它使用 4 个内核(我拥有的最大值)。在处理过程中,我所有的核心都处于 100% 的状态,而且运行速度很快。但是,现在我将代码移至服务器以处理大量数据,并将内核数更新为 30(服务器有 32 个内核)。

但是,当我启动该进程时,它似乎正确启动了 30 个线程,但我的大多数内核都处于 0%,除了其中约 5 个在 20% 左右,但是这个过程很长。'top' 为我的进程报告 145% CPU,当有 30 个内核时,它应该更接近 3000%。

这是我的并行代码:

#pragma omp parallel for num_threads(N_CORES) schedule(static,1) shared(viImgCounts, viLabelCounts, vfLabelLogLikelihood, ppfMatchingVotes)
      for (int n = 0; n < N_CORES; ++n)
        {
          // Each thread process it's range of files
          for (int i = vpChunkStartEnd[n].first; i <= vpChunkStartEnd[n].second; i++)
            {
              printf("Searching %d of %d ... ", i, vvInputFeats.size());
              ProcessingData(i, viImgCounts, viLabelCounts, vfLabelLogLikelihood, ppfMatchingVotes, ppiLabelVotes);
              printf("\n");
            }
        }

在这里,我将 N_CORES 定义为 30 (#define N_CORES 30)。

每个线程都有一系列文件要处理(vpChunkStartEnd),所以在我的理解中,它们应该以 100% 的速度一起运行,处理它们的文件列表。我不确定为什么这里没有发生(当它在我的 4 核工作站上时)。

我是否忘记了#pragma 中的某些内容?

谢谢你。

编辑: 这是我得到的输出:

Searching 6750 of 7482 ... Searching 4000 of 7482 ... Searching 3750 of 7482 ... Searching 5000 of 7482 ... Searching 0 of 7482 ... Searching 500 of 7482 ... Searching 2250 of 7482 ... Searching 5500 of 7482 ... |0|Searching 5750 of 7482 ... |0|Searching 6000 of 7482 ... |0|Searching 7250 of 7482 ... |0|Searching 7000 of 7482 ... Searching 750 of 7482 ... Searching 3250 of 7482 ... Searching 1000 of 7482 ... |1||1|Searching 1500 of 7482 ... Searching 2750 of 7482 ... Searching 2000 of 7482 ... |1|Searching 5250 of 7482 ... |0||0|Searching 3500 of 7482 ... Searching 4500 of 7482 ... |0|Searching 1250 of 7482 ... |1|Searching 4750 of 7482 ... |0||1|Searching 6250 of 7482 ... |1|Searching 250 of 7482 ... |1|Searching 4250 of 7482 ... |0|Searching 1750 of 7482 ... |0||0||0||1||0||1||1|Searching 3000 of 7482 ... |0||0||1||0|Searching 2500 of 7482 ... |0|Searching 6500 of 7482 ... |1||0|
Searching 4001 of 7482 ... |0|
Searching 3001 of 7482 ... |0|
Searching 3251 of 7482 ... |0|
Searching 6751 of 7482 ... |1|
Searching 2251 of 7482 ... |0|
Searching 1751 of 7482 ... |0|
Searching 1501 of 7482 ... |0|
Searching 5501 of 7482 ... |1|
Searching 5001 of 7482 ... |1|
Searching 7001 of 7482 ... |1|
Searching 1251 of 7482 ... |0|
Searching 5251 of 7482 ... |1|
Searching 4501 of 7482 ... |1|
Searching 3751 of 7482 ... |0|
Searching 5751 of 7482 ... |1|
Searching 2751 of 7482 ... |0|
Searching 4002 of 7482 ... |0|
Searching 4751 of 7482 ... |1|
Searching 6251 of 7482 ... |1|
Searching 6501 of 7482 ... |1|
Searching 6001 of 7482 ... |1|
Searching 2001 of 7482 ... |0|
Searching 3501 of 7482 ... |0|
Searching 3252 of 7482 ... |0|

我们可以看到,在第一次迭代时,因为多个线程并行运行,文本全部被打乱,但之后,一切都被正确打印,并且大多数内核都处于空闲状态,所以感觉每次迭代都在等待前一个完成开始。他们没有同时完成循环可能只是巧合,但这会持续数千个文件,所以这将是一个巨大的巧合,而且大多数核心都处于空闲状态,让我认为其他迭代未正确并行化。

编辑2:好吧,奇怪的事情发生了。正如评论中提到的,'top' 告诉我我没有更多可用内存(仅 800MB),但资源管理器仅显示 50% 已使用(48GB)。我开始使用大约 20GB 运行一个软件,然后我把它杀死了。现在“顶部”显示 20GB 可用内存(使用的内存没有移动),现在我的程序以 1000% 的速度运行。所以我想知道 top 关于可用内存是否真的是正确的,这就是减慢我的程序的原因,但我也想知道为什么启动一个使用大量内存的程序并杀死它会释放该内存。这可能是由于内存泄漏导致内存消失了,并且程序请求了内存,因此泄漏的内存已被重用,并且当程序被杀死时,它会将其返回到“空闲”内存,

编辑3:好的,看来这不是问题。CPU 使用率恢复到 350%(大多数内核再次空闲),甚至 top 说我有 20GB 的可用内存。CPU使用率如此不均匀,我不明白。

是否有可能因为我将“共享”与某个变量一起使用,线程必须等待其他线程完成此变量才能访问它(如互斥锁)?因为我确保没有线程会写在这个变量的相同位置。这可能是它减慢我的代码的原因吗?

编辑 4:好的,现在我恢复了一些内存,它似乎使用了我所有的内核,但它们都在 30%,这次我有足够的可用内存(其中很少有 50%),但它不是疯狂的。我应该增加线程数吗?或者他们只是要排队(这不会提高 CPU 使用率)?

编辑 5:我使用 htop 获取一些统计信息,似乎我一次平均有 10 个正在运行的任务,如果我启动了 30 个线程(这就是我所拥有的),这对应于 30% 的 CPU 使用率。所以似乎不是我所有的线程一直在运行,但我不明白为什么。我可能需要对其进行分析,但我使用 valgrind 进行了测试,使用它的速度很慢(我需要先加载一堆数据,而使用 valgrind 则不可行)。

编辑 6:所以我编写了一个虚拟程序,使用与代码中相同的 omp 参数和相同的结构,但我没有调用 ProcessingData 函数,而是增加了一个变量,它创建了大约 30 个正在运行的任务,我达到 100% 的 CPU 使用率。但是,当我使用我的函数时,它会创建 5-15 个正在运行的任务,而且我从未达到 100%。我使用 htop 查看我的线程的状态,他们在“D”状态(不间断睡眠)中花费了很多时间。我想我需要对他们进行分析以了解原因。

标签: c++parallel-processingopenmp

解决方案


我们看不到您的其余代码,但您可能想尝试使用以下函数来弄清楚发生了什么:

omp_get_num_threads() // get the number of threads operating in a parallel region    

和:

omp_set_num_threads(numThreads) // set the number of threads used in a parallel region

与仅查看处理器活动监视器相比,这将使您更好地了解处理器利用率。

以下是更详细描述此问题的其他一些很好的答案:

最后,重要的是要记住核心与线程不同。您可能知道这一点,但只是想保持术语直截了当。


推荐阅读