操作系统级更改以加速 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(因此无法安排它们)。
要解决这个问题,您可以:
- 使用进程而不是线程(正如您在问题中提到的)
- 使用没有全局解释器锁的其他 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)
在我使用的 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(因此无法安排它们)。
要解决这个问题,您可以:
- 使用进程而不是线程(正如您在问题中提到的)
- 使用没有全局解释器锁的其他 python 解释器。
我认为您的基准测试并不像您认为的那样作为独立任务执行。你没有显示 function
的代码,但我怀疑它做了一些同步。
我写了以下基准测试。如果我 运行 使用 --fork
或 --mp
选项的脚本,我总是得到 400 % CPU 利用率(在我的四核机器上)和相当的总执行时间大约 18 秒。但是,如果使用 --threads
选项调用,该程序将按顺序有效 运行s,仅实现大约 100% CPU 的利用率并且需要一分钟才能完成,原因是
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)