2

我有一个使用库的有点大的代码numpy, scipy, sklearn, matplotlib。我需要限制 CPU 使用率以阻止它消耗我计算集群中的所有可用处理能力。按照这个答案,我实现了以下代码块,该代码块在脚本运行后立即执行:

import os
parallel_procs = "4"
os.environ["OMP_NUM_THREADS"] = parallel_procs
os.environ["MKL_NUM_THREADS"] = parallel_procs
os.environ["OPENBLAS_NUM_THREADS"] = parallel_procs
os.environ["VECLIB_MAXIMUM_THREADS"] = parallel_procs
os.environ["NUMEXPR_NUM_THREADS"] = parallel_procs

我的理解是,这应该将使用的核心数量限制为 4,但显然这并没有发生。这是htop为我的用户和该脚本显示的内容:

在此处输入图像描述

有 16 个进程,其中 4 个显示 CPU 百分比高于 100%。这是以下内容的摘录lscpu

CPU(s):              48
On-line CPU(s) list: 0-47
Thread(s) per core:  2
Core(s) per socket:  12
Socket(s):           2

我还在multiprocessing我的代码中使用该库。我使用multiprocessing.Pool(processes=4). 如果没有上面显示的代码块,脚本坚持使用尽可能多的内核显然multiprocessing完全忽略了。

那么我的问题是:当我使用上面的代码时,我有什么限制?我应该如何解释htop输出?

4

1 回答 1

1

(作为评论可能会更好,如果有更好的答案,请随时删除它,因为它基于我使用库的经验。)

在我的代码的多处理部分时,我遇到了类似的问题。如果您使用 BLAS 或 MKL 编译库(或者如果您从中提取它们的conda repo 还包含 BLAS/MKL 库),则numpy/库似乎会在您执行矢量化操作时启动额外的线程,以加速某些计算。scipy

在单个进程中运行脚本时这很好,因为它会产生最多由OPENBLAS_NUM_THREADSor指定的线程数MKL_NUM_THREADS(取决于您是否有 BLAS 库或 MKL 库 - 您可以使用 识别哪个numpy.__config__.show()),但如果您明确使用 a multiprocesing.Pool,那么您可能想要控制进程的数量multiprocessing- 在这种情况下,设置n=1导入 numpy 和 scipy 之前)或一些小数字以确保您没有超额订阅是有意义的:

n = '1'
os.environ["OMP_NUM_THREADS"] = n
os.environ["MKL_NUM_THREADS"] = n

如果设置multiprocessing.Pool(processes=4),它将使用 4*n进程(n每个进程中的线程)。在您的情况下,您似乎有一个由 4 个进程组成的池,每个进程启动 4 个线程,因此有 16 个 python 进程。

假设每个核心有一个 CPUhtop输出为100% 。由于 Linux 机器将线程解释为 CPU(我在这里的术语可能有误),如果每个 CPU 有 4 个线程,则意味着满载实际上是 400%。这可能不会被最大化,这取决于正在执行的操作(以及缓存,因为您的机器看起来是超线程的)。

因此,如果您在单个进程/单线程中的部分代码中执行 numpy/scipy 操作,最好设置一个更大的n,但对于多处理部分,最好设置一个更大的池和单或小n。不幸的是,如果您通过环境标志传递标志,则只能在脚本开头设置一次。如果你想动态设置它,我在某个地方的 numpy 问题讨论中看到你应该使用threadpoolctl(如果我能再次找到它,我会添加一个链接)。

于 2021-05-14T14:00:17.060 回答