当我限制“线程”的数量时,我在设置什么?

问题描述

我有一段使用库 numpy,scipy,sklearn,matplotlib 的大代码。我需要限制 cpu 使用率以阻止它消耗我的计算集群中的所有可用处理能力。在 this answer 之后,我实现了脚本运行后立即执行的以下代码块:

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 为我的用户和该脚本显示内容

enter image description here

有 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 输出

解决方法

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

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

在单个进程中运行脚本时这很好,因为它会产生最多由 OPENBLAS_NUM_THREADSMKL_NUM_THREADS 指定的数量的线程(取决于您有 BLAS 库还是 MKL 库 -您可以使用 numpy.__config__.show()) 确定哪个,但如果您明确使用 multiprocesing.Pool,那么您可能希望控制 multiprocessing 中的进程数 - 在这种情况下,这是有道理的设置 n=1before 导入 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 进程。

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

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