操作系统级更改加速Python的多处理?

时间:2015-12-15 00:17:02

标签: python subprocess python-multiprocessing

在我使用的linux系统上,调度程序不是很慷慨,给从python的多处理模块产生的子进程提供了cpu时间。在4核机器上使用4个子程序时,根据ps,我获得了大约22%的CPU。但是,如果子进程是shell的子进程,而不是python程序,则它会达到接近100%的CPU。但是多处理是一个比手动分割我的数据更好的接口,并为每个分割运行单独的python程序,并且最好的两个世界(代码组织和高CPU利用率)。我尝试设置流程'对-20的好感,但这没有帮助。

我想知道用一些选项重新编译linux内核是否有助于调度程序为python多处理工作者提供更多的CPU时间。也许有相关的配置选项?

我使用的确切版本是:

$ uname -a
Linux <hostname> 3.19.0-39-generic #44~14.04.1-Ubuntu SMP Wed Dec 2 10:00:35 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

如果这可能与我使用多处理的方式有关,则其格式如下:

 with Pool(4) as p:
     p.map(function,data)

更新:    这不是一个可重现的问题。这里报告的结果来自几天前,我再次运行测试,多处理过程和我希望的一样快。也许这个问题应该被删除,误导人们对multiprocessing期望的表现不会有好处。

2 个答案:

答案 0 :(得分:2)

我不相信你的基准测试是作为独立任务执行的,正如你可能认为的那样。你没有显示function的代码,但我怀疑它有一些同步。

我写了以下基准。如果我使用--fork--mp选项运行脚本,我总是获得400%的CPU利用率(在我的四核机器上),并且可比的总执行时间大约为18秒。但是,如果使用--threads选项调用,则程序有效地按顺序运行,只需要大约100%的CPU利用率,并且需要一分钟才能完成mentioned dave的原因。

import multiprocessing
import os
import random
import sys
import threading


def find_lucky_number(x):
    prng = random.Random()
    prng.seed(x)
    for i in range(100000000):
        prng.random()
    return prng.randint(0, 100)

def with_threading(inputs):
    callback = lambda x : print(find_lucky_number(x))
    threads = [threading.Thread(target=callback, args=(x,)) for x in inputs]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

def with_multiprocessing(inputs):
    with multiprocessing.Pool(len(inputs)) as pool:
        for y in pool.map(find_lucky_number, inputs):
            print(y)

def with_forking(inputs):
    pids = list()
    for x in inputs:
        pid = os.fork()
        if pid == 0:
            print(find_lucky_number(x))
            sys.exit(0)
        else:
            pids.append(pid)
    for pid in pids:
        os.waitpid(pid, 0)

if __name__ == '__main__':
    inputs = [1, 2, 3, 4]
    if sys.argv[1] == '--threads':
        with_threading(inputs)
    if sys.argv[1] == '--mp':
        with_multiprocessing(inputs)
    elif sys.argv[1] == '--fork':
        with_forking(inputs)
    else:
        print("What should I do?", file=sys.stderr)
        sys.exit(1)

答案 1 :(得分:1)

欢迎使用CPython Global Interpreter Lock。你的线程显示为与linux内核不同的进程(这就是通常在Linux中实现线程的方式:每个线程都有自己的进程,所以内核可以安排它们)。

那么为什么Linux的调度不止一次运行(这就是为什么你的4核机器平均减去25%减去一点开销)? python解释器在解释每个线程时持有一个锁,从而阻止其他线程运行(因此无法安排它们)。

要解决这个问题,你可以:

  1. 使用流程而不是线程(正如您在问题中提到的那样)
  2. 使用不具备Global Interpreter Lock的其他python解释器。