我正在使用OpenMP进行这样的循环:
#pragma omp parallel for
for (int out = 1; out <= matrix.rows; out++)
{
...
}
我在64台CPU的机器上进行了大量的计算。这很有效但我的问题是:
我是否在这台机器上打扰其他用户?通常它们只运行单线程程序。他们仍然会100%运行吗?显然我会打扰其他多线程程序,但是我会打扰单线程程序吗?
如果是的话,我能预先知道吗?我认为a可以设置omp_set_num_threads
的最大CPU数。我可以将其设置为60,但我不认为这是最好的解决方案。
理想的解决方案不会干扰其他单线程程序,但会占用尽可能多的CPU。
答案 0 :(得分:1)
每个多任务操作系统都有一个称为进程调度程序的东西。这是一个OS组件,用于决定运行每个进程的位置和时间。调度程序通常在做出的决策中非常顽固,但这些通常会受到各种用户提供的策略和提示的影响。几乎所有调度程序的默认配置都是尝试将负载分散到所有可用的CPU上,这通常会导致进程从一个CPU迁移到另一个CPU。幸运的是,任何现代操作系统除了&#34;最先进的桌面操作系统&#34; (a.k.a. OS X)支持称为处理器关联的东西。每个进程都有一组允许执行的处理器 - 该进程的所谓CPU亲和性集。通过将不相交的亲和性集配置到各种进程,可以使这些进程并发执行而不会相互消耗CPU时间。 Linux,FreeBSD(使用ULE调度程序),Windows NT(这也包括自Windows XP以来的所有桌面版本)以及可能的其他操作系统(但不是OS X)支持显式CPU亲和性。然后,每个操作系统都提供一组内核调用来操作关联,也可以提供一种工具,无需编写特殊程序。在Linux上,这是使用sched_setaffinity(2)
系统调用和taskset
命令行工具完成的。也可以通过创建cpuset
实例来控制亲和力。在Windows上,使用SetProcessAffinityMask()
和/或SetThreadAffinityMask()
,可以在任务管理器中从给定进程的上下文菜单中设置亲和力。还可以在启动新进程时将所需的关联掩码指定为START
shell命令的参数。
这与OpenMP的关系是,所列出的操作系统的大多数OpenMP运行时支持一种形式或另一种方式,以指定每个OpenMP线程所需的CPU关联。最简单的控件是OMP_PROC_BIND
环境变量。这是一个简单的开关 - 当设置为TRUE
时,它指示OpenMP运行时为&#34;绑定&#34;每个线程,即为其提供仅包括单个CPU的亲和性集。线程到CPU的实际位置取决于实现,每个实现都提供了自己的方法来控制它。例如,GNU OpenMP运行时(libgomp
)读取GOMP_CPU_AFFINITY
环境变量,而英特尔OpenMP运行时(不久前开源)读取KMP_AFFINITY
环境变量。
这里的基本原理是,您可以限制程序的亲和力,以便仅使用所有可用CPU的子集。然后剩余的进程主要被调度到其余的CPU,但是只有在手动设置它们的亲和力时才能保证这一点(这只有在你拥有root / Administrator访问权限时才有效,否则你只能修改那些进程的亲和性)。你拥有)。
值得一提的是,经常(但并非总是)使用比亲和集中的CPU数更多的线程运行是没有意义的。例如,如果您将程序限制为在60个CPU上运行,那么使用64个线程将导致某些CPU超额订阅以及线程之间的分时。这将使一些线程比其他线程运行得慢。大多数OpenMP运行时的默认调度是schedule(static)
,因此并行区域的总执行时间由最慢线程的执行时间决定。如果一个线程与另一个线程共用一个,那么两个线程的执行速度将比那些没有分时共享的线程执行得慢,并且整个并行区域会被延迟。这不仅降低了并行性能,而且还导致浪费的周期,因为更快的线程只是等待什么都不做(可能在并行区域末端的隐式屏障处繁忙循环)。解决方案是使用动态调度,即:
#pragma omp parallel for schedule(dynamic,chunk_size)
for (int out = 1; out <= matrix.rows; out++)
{
...
}
其中chunk_size
是每个线程获得的迭代块的大小。整个迭代空间被划分为chunk_size
次迭代的块,并以先来先服务的方式提供给工作线程。块大小是一个重要参数。如果它太低(默认值为1),那么管理动态调度的OpenMP运行时可能会产生巨大的开销。如果它太高,那么每个线程可能没有足够的工作可用。块大小大于maxtrix.rows / #threads
是没有意义的。
动态调度允许您的程序适应可用的CPU资源,即使它们不统一,例如如果有其他进程正在运行并且与当前进程共享时间。但它带来了一个问题:像64核的大系统通常是ccNUMA(缓存一致的非统一内存访问)系统,这意味着每个CPU都有自己的内存块并且可以访问内存块。其他CPU是昂贵的(例如,花费更多时间和/或提供更少带宽)。动态调度往往会破坏数据局部性,因为无法确定驻留在一个NUMA上的内存块不会被另一个NUMA节点上运行的线程利用。当数据集很大且不适合CPU缓存时,这一点尤为重要。因此YMMV。
答案 1 :(得分:0)
在操作系统中将您的进程置于低优先级。根据需要使用许多资源。如果其他人需要这些资源,操作系统将确保提供这些资源,因为它们具有更高(即正常)的优先级。如果没有其他用户,您将获得所有资源。