【问题标题】:What am I setting when I limit the number of "threads"?当我限制“线程”的数量时,我在设置什么?
【发布时间】:2021-08-04 01:46:47
【问题描述】:

我有一个使用库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 为我的用户和该脚本显示的内容:

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

【问题讨论】:

  • 您的 Python 代码是多线程的还是故意使用多个进程(如 multiprocessing 模块)?您设置的环境变量可能只会对可能在内部使用线程的库代码产生影响。但是,如果您使用自己的线程,您可能仍会并行多次调用这些库。
  • 我确实在我的代码中使用multiprocessing 库。我使用multiprocessing.Pool(processes=4) 设置了相同数量的进程。我不得不添加问题中显示的代码块,否则脚本坚持使用尽可能多的内核,显然忽略了multiprocessing
  • 正在使用的内核总数可能是进程数和线程数的乘积(例如 4*4)。库代码不会知道multiprocessing 代码(或者实际上可能是并行运行的其他库代码)。如果您使用的库执行自己的线程,您可能不需要添加自己的并行处理。
  • 这似乎与最近的问题有关:stackoverflow.com/questions/67474542/…

标签: python multithreading numpy htop


【解决方案1】:

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

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

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

【讨论】:

  • 这是一个很详细的回答蒂姆。谢谢!
  • 抱歉,我无法提供更好的资源 - 当我只是需要一些东西来工作时,这是从经验中学习的。
猜你喜欢
  • 2010-11-27
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 2020-05-13
  • 1970-01-01
  • 2012-05-24
  • 1970-01-01
相关资源
最近更新 更多