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

Operating-System-Level Changes To Speed Up Python's Multiprocessing?

在我使用的 linux 系统上,调度程序不是很慷慨地给 cpu 时间给从 python 的多处理模块产生的子进程。根据 ps,在 4 核机器上使用 4 个子进程时,我得到大约 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.

的预期性能是不好的

欢迎使用 CPython 全局解释器锁。您的线程在 linux 内核中显示为不同的进程(这通常是 Linux 中线程的实现方式:每个线程都有自己的进程,因此内核可以调度它们)。

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

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

  1. 使用进程而不是线程(正如您在问题中提到的)
  2. 使用没有全局解释器锁的其他 python 解释器。

我认为您的基准测试并不像您认为的那样作为独立任务执行。你没有显示 function 的代码,但我怀疑它做了一些同步。

我写了以下基准测试。如果我 运行 使用 --fork--mp 选项的脚本,我总是得到 400 % CPU 利用率(在我的四核机器上)和相当的总执行时间大约 18 秒。但是,如果使用 --threads 选项调用,该程序将按顺序有效 运行s,仅实现大约 100% CPU 的利用率并且需要一分钟才能完成,原因是 by 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)