Python 多处理:有和没有池
Python multiprocessing: with and without pooling
我试图了解 Python 的多处理,并设计了以下代码来测试它:
import multiprocessing
def F(n):
if n == 0: return 0
elif n == 1: return 1
else: return F(n-1)+F(n-2)
def G(n):
print(f'Fibbonacci of {n}: {F(n)}')
processes = []
for i in range(25, 35):
processes.append(multiprocessing.Process(target=G, args=(i, )))
for pro in processes:
pro.start()
当我运行它的时候,我告诉我计算时间大约是6.65s。
然后我写了下面的代码,我认为它在功能上等同于后者:
from multiprocessing.dummy import Pool as ThreadPool
def F(n):
if n == 0: return 0
elif n == 1: return 1
else: return F(n-1)+F(n-2)
def G(n):
print(f'Fibbonacci of {n}: {F(n)}')
in_data = [i for i in range(25, 35)]
pool = ThreadPool(10)
results = pool.map(G, in_data)
pool.close()
pool.join()
而它的运行宁时间将近12s。
为什么第二个几乎是第一个的两倍?他们不应该是等价的吗?
(注意。我是 运行ning Python 3.6,但也在 3.52 上测试了类似的代码,结果相同。)
第二个花费的时间是第一个的两倍的原因可能是由于 CPython 全局解释器锁。
来自http://python-notes.curiousefficiency.org/en/latest/python3/multicore_python.html:
[...] the GIL effectively restricts bytecode execution to a single core, thus rendering pure Python threads an ineffective tool for distributing CPU bound work across multiple cores.
如您所知,multiprocessing.dummy
是 threading
模块的包装器,因此您创建的是线程,而不是进程。全局解释器锁,这里有一个 CPU 绑定任务,与简单地在单个线程中顺序执行斐波那契计算没有太大区别(除了你已经添加了一些 thread-management/context-switching 开销)。
在 "true multiprocessing" 版本中,每个进程中只有一个线程,每个线程都使用自己的 GIL。因此,您实际上可以利用多个处理器来提高速度。
对于这个特定的处理任务,没有使用多线程优于多进程的显着优势。如果您只有一个处理器,那么在单个 thread/process 上使用 either 多个进程 或 多个线程没有任何优势(在事实上,两者都只是为您的任务增加了上下文切换开销。
(FWIW:multiprocessing
版本中的 join
显然是由 python 运行时自动完成的,因此添加显式 join
似乎没有使用 time(1)
对我的测试有任何影响。顺便说一下,如果你 确实 想要添加 join
,你应该添加一个 join
处理的第二个 循环。将 join
添加到现有循环将简单地序列化您的进程。)
我试图了解 Python 的多处理,并设计了以下代码来测试它:
import multiprocessing
def F(n):
if n == 0: return 0
elif n == 1: return 1
else: return F(n-1)+F(n-2)
def G(n):
print(f'Fibbonacci of {n}: {F(n)}')
processes = []
for i in range(25, 35):
processes.append(multiprocessing.Process(target=G, args=(i, )))
for pro in processes:
pro.start()
当我运行它的时候,我告诉我计算时间大约是6.65s。
然后我写了下面的代码,我认为它在功能上等同于后者:
from multiprocessing.dummy import Pool as ThreadPool
def F(n):
if n == 0: return 0
elif n == 1: return 1
else: return F(n-1)+F(n-2)
def G(n):
print(f'Fibbonacci of {n}: {F(n)}')
in_data = [i for i in range(25, 35)]
pool = ThreadPool(10)
results = pool.map(G, in_data)
pool.close()
pool.join()
而它的运行宁时间将近12s。
为什么第二个几乎是第一个的两倍?他们不应该是等价的吗?
(注意。我是 运行ning Python 3.6,但也在 3.52 上测试了类似的代码,结果相同。)
第二个花费的时间是第一个的两倍的原因可能是由于 CPython 全局解释器锁。
来自http://python-notes.curiousefficiency.org/en/latest/python3/multicore_python.html:
[...] the GIL effectively restricts bytecode execution to a single core, thus rendering pure Python threads an ineffective tool for distributing CPU bound work across multiple cores.
如您所知,multiprocessing.dummy
是 threading
模块的包装器,因此您创建的是线程,而不是进程。全局解释器锁,这里有一个 CPU 绑定任务,与简单地在单个线程中顺序执行斐波那契计算没有太大区别(除了你已经添加了一些 thread-management/context-switching 开销)。
在 "true multiprocessing" 版本中,每个进程中只有一个线程,每个线程都使用自己的 GIL。因此,您实际上可以利用多个处理器来提高速度。
对于这个特定的处理任务,没有使用多线程优于多进程的显着优势。如果您只有一个处理器,那么在单个 thread/process 上使用 either 多个进程 或 多个线程没有任何优势(在事实上,两者都只是为您的任务增加了上下文切换开销。
(FWIW:multiprocessing
版本中的 join
显然是由 python 运行时自动完成的,因此添加显式 join
似乎没有使用 time(1)
对我的测试有任何影响。顺便说一下,如果你 确实 想要添加 join
,你应该添加一个 join
处理的第二个 循环。将 join
添加到现有循环将简单地序列化您的进程。)