Python 多进程:限制使用的核心数量

11 投票
5 回答
10881 浏览
提问于 2025-04-15 15:06

我想知道怎么把N个独立的任务分配给正好M个处理器,而我的机器有L个核心,并且L大于M。我不想用完所有的处理器,因为我还想保留一些处理器用于输入输出操作。之前尝试的解决方案似乎都把任务分配给了所有处理器,导致系统变得很慢。

我觉得使用多进程模块可能是个好办法。

我在做数值模拟。我的背景是物理学,而不是计算机科学,所以很遗憾,我经常对一些标准的任务模型,比如服务器/客户端、生产者/消费者等讨论理解得不够透彻。

以下是我尝试过的一些简化模型:

假设我有一个函数 run_sim(**kwargs)(下面会详细介绍),它用来运行模拟,并且我有一长串用于模拟的参数,而我的机器有8个核心。

from multiprocessing import Pool, Process

#using pool
p = Pool(4)
p.map(run_sim, kwargs)

# using process
number_of_live_jobs=0
all_jobs=[]
sim_index=0
while sim_index < len(kwargs)+1:
   number_of_live_jobs = len([1 for job in all_jobs if job.is_alive()])
   if number_of_live_jobs <= 4:
      p = Process(target=run_sim, args=[], kwargs=kwargs[sim_index])
      print "starting job", kwargs[sim_index]["data_file_name"]
      print "number of live jobs: ", number_of_live_jobs
      p.start()
      p.join()
      all_jobs.append(p)
      sim_index += 1

当我用“top”命令查看处理器使用情况,然后按“1”键时,似乎所有处理器都在被使用。虽然我可能误解了“top”的输出,但如果 run_simulation() 这个函数很占用处理器资源,机器就会变得非常卡。

假设的模拟和数据:

# simulation kwargs
numbers_of_steps = range(0,10000000, 1000000)
sigmas = [x for x in range(11)]
kwargs = []
for number_of_steps in numbers_of_steps:
   for sigma in sigmas:
      kwargs.append(
         dict(
            number_of_steps=number_of_steps,
            sigma=sigma,
            # why do I need to cast to int?
            data_file_name="walk_steps=%i_sigma=%i" % (number_of_steps, sigma),
            )
         )

import random, time
random.seed(time.time())

# simulation of random walk
def run_sim(kwargs):
   number_of_steps = kwargs["number_of_steps"]
   sigma = kwargs["sigma"]
   data_file_name = kwargs["data_file_name"]
   data_file = open(data_file_name+".dat", "w")
   current_position = 0
   print "running simulation", data_file_name
   for n in range(int(number_of_steps)+1):
      data_file.write("step number %i   position=%f\n" % (n, current_position))
      random_step = random.gauss(0,sigma)
      current_position += random_step

   data_file.close()

5 个回答

2

在我的双核电脑上,进程的总数是被认可的,也就是说,如果我执行

p = Pool(1)

那么我在任何时候只看到一个CPU在工作。这个进程可以自由地转移到另一个处理器上,但这时另一个处理器就闲着。我不明白你们的所有处理器怎么能同时工作,所以我不太明白这和你们的输入输出问题有什么关系。当然,如果你的模拟是受输入输出限制的,那么无论核心使用情况如何,你都会感觉到输入输出很慢...

2

你可能想看看下面这个软件包:

http://pypi.python.org/pypi/affinity

这个软件包使用了两个功能,分别叫做sched_setaffinity和sched_getaffinity。

不过它的一个缺点是,它主要是针对Linux系统的。

3

如果你在使用Linux系统,启动程序时可以用taskset命令。

通过fork(2)创建的子进程会继承父进程的CPU亲和性掩码。这个亲和性掩码在执行execve(2)时会被保留。

TASKSET(1)
Linux用户手册
TASKSET(1)

名称 taskset - 获取或设置一个进程的CPU亲和性

语法 taskset [选项] 掩码 命令 [参数]... taskset [选项] -p [掩码] 进程ID

描述 taskset用于设置或获取一个正在运行的进程的CPU亲和性,给定它的进程ID,或者用指定的CPU亲和性启动一个新的命令。CPU亲和性是调度器的一个属性,它将一个进程“绑定”到系统上的一组特定CPU。Linux调度器会遵循这个CPU亲和性,进程不会在其他CPU上运行。需要注意的是,Linux调度器还支持自然CPU亲和性:调度器会尽量让进程保持在同一个CPU上,以提高性能。因此,强制指定某个CPU亲和性在某些应用中才有用。

CPU亲和性用位掩码表示,最低位对应第一个逻辑CPU,最高位对应最后一个逻辑CPU。并不是所有CPU在某个系统上都存在,但掩码可以指定比实际存在的CPU更多的CPU。如果获取的掩码中没有对应于系统上实际存在的CPU的位,就会返回错误。掩码通常以十六进制表示。

撰写回答