我一直在努力了解有关Python multiprocessing
模块的更多信息,并评估流程之间通信的不同技术。我编写了一个基准测试,用于比较Pipe
,Queue
和Array
(来自multiprocessing
)的性能,以便在进程之间传输numpy
数组。可以找到完整的基准here。这是Queue
的测试片段:
def process_with_queue(input_queue, output_queue):
source = input_queue.get()
dest = source**2
output_queue.put(dest)
def test_with_queue(size):
source = np.random.random(size)
input_queue = Queue()
output_queue = Queue()
p = Process(target=process_with_queue, args=(input_queue, output_queue))
start = timer()
p.start()
input_queue.put(source)
result = output_queue.get()
end = timer()
np.testing.assert_allclose(source**2, result)
return end - start
我在我的Linux笔记本电脑上运行了这个测试,并获得了数组大小为1000000的以下结果:
Using mp.Array: time for 20 iters: total=2.4869s, avg=0.12435s
Using mp.Queue: time for 20 iters: total=0.6583s, avg=0.032915s
Using mp.Pipe: time for 20 iters: total=0.63691s, avg=0.031845s
我有点惊讶地看到Array
表现得如此糟糕,因为它使用共享内存并且可能不需要酸洗,但我认为必须在numpy
中进行一些复制我可以& #39; t控制。
但是,我在Macbook上运行了相同的测试(数组大小为1000000),得到了以下结果:
Using mp.Array: time for 20 iters: total=1.6917s, avg=0.084587s
Using mp.Queue: time for 20 iters: total=2.3478s, avg=0.11739s
Using mp.Pipe: time for 20 iters: total=8.7709s, avg=0.43855s
真正的时间差异并不令人惊讶,因为当然不同的系统会表现出不同的性能。什么 令人惊讶的是相对时间的差异。
有什么可以解释这个?这对我来说是一个非常令人惊讶的结果。看到Linux和Windows,OSX和Windows之间存在如此明显的差异,我不会感到惊讶,但我有点认为这些事情在OSX和Linux之间的表现非常相似。
This question解决了Windows和OSX之间的性能差异,这似乎更令人期待。
答案 0 :(得分:4)
使用Array
中的multiprocessing
使用C types Python library进行C调用以设置数组的内存。在Linux上比在OSX上花费更多的时间。您也可以使用pypy在OSX上观察到这一点。使用pypy(以及GCC和LLVM)设置内存所需的时间比在OSX(使用Clang)上使用python3的时间更长。
主要区别在于multiprocessing
的实现,它在OSX下的工作方式不同于Windows。最重要的区别是multiprocessing
启动新进程的方式。可以通过三种方式完成此操作:使用spawn
,fork
或forkserver
。 Windows下的默认(且仅受支持的)方式为spawn
。 * nix(包括OSX)下的默认方式是fork
。 multiprocessing
文档的Contexts and start methods部分对此进行了记录。
导致结果偏差的另一个原因是迭代次数少。
如果增加迭代次数并计算每个时间单位的已处理函数调用次数,则这三种方法之间将获得相对一致的结果。
我删除了您的timeit
计时器函数,并将您的代码包装在cProfile
分析器中。
我添加了这个包装函数:
def run_test(iters, size, func):
for _ in range(iters):
func(size)
然后我将main()
中的循环替换为:
for func in [test_with_array, test_with_pipe, test_with_queue]:
print(f"*** Running {func.__name__} ***")
pr = cProfile.Profile()
pr.enable()
run_test(args.iters, args.size, func)
pr.disable()
ps = pstats.Stats(pr, stream=sys.stdout)
ps.strip_dirs().sort_stats('cumtime').print_stats()
我看到的是队列比管道快,比管道快。无论使用哪种平台(OSX / Linux / Windows),Queue的速度都比Pipe快2至3倍。在OSX和Windows上,Pipe比Array快1.2到1.5倍。但是在Linux上,Pipe比Array快3.6倍。换句话说,在Linux上,Array的速度要比Windows和OSX慢得多。这很奇怪。
使用cProfile数据,我比较了OSX和Linux之间的性能比。有两个函数调用需要很长时间:Array
中的RawArray
和sharedctypes.py
。这些函数仅在Array场景中调用(不在Pipe或Queue中)。在Linux上,这些调用几乎花费70%的时间,而在OSX上仅花费42%的时间。所以这是一个主要因素。
如果放大to the code,我们会看到Array
(第84行)调用RawArray
,而RawArray
(第54行)除了调用ctypes.memset
(documentation)。因此,我们有一个嫌疑犯。让我们测试一下。
以下代码使用timeit来测试将1 MB内存缓冲区设置为“ A”的性能。
import timeit
cmds = """\
import ctypes
s=ctypes.create_string_buffer(1024*1024)
ctypes.memset(ctypes.addressof(s), 65, ctypes.sizeof(s))"""
timeit.timeit(cmds, number=100000)
在MacBookPro和Linux服务器上运行该命令,确认了此行为在Linux上比在OSX上运行慢得多。知道pypy是在使用GCC和Apples LLVM编译的OSX上,这比Linux直接类似于Clang编译的OSX更类似于Linux世界。通常,Python程序在pypy上的运行速度比CPython快,但是上面的代码在pypy上(在相同的硬件上)运行的速度慢6.4倍。
我对C工具链和C库的了解有限,因此我无法深入研究。所以我的结论是: 使用Array可以更快地使用OSX和Windows,因为在Linux上对C库的内存调用会减慢Array的速度 。
接下来,我在OSX和Windows下的双引导MacBook Pro上运行此程序。优点是底层硬件相同。只有操作系统不同。我将迭代次数增加到1000,并将大小增加到10.000。
结果如下:
我们可以看到:
spawn
)相比,Windows实现(使用fork
)进行的调用更多; 虽然不是立即显而易见,但需要注意的是,如果查看每个调用的平均时间,则三种多重处理方法(数组,队列和管道)之间的相对模式是相同的(参见下图)。换句话说: OSX和Windows中的Array,Queue和Pipe之间的性能差异可以由两个因素完全解释:1.两个平台之间的Python性能差异; 2.两种平台处理多重处理的方式不同。
换句话说:multiprocessing
文档的Contexts and start methods部分解释了呼叫数量的差异。 OSX和Windows之间的Python性能差异解释了执行时间的差异。如果剔除这两个组件,则如下图所示,在OSX和Windows上,Array,Queue和Pipe的相对性能(或多或少)是可比的。
答案 1 :(得分:-3)
好吧,当我们谈论python的多进程时,会发生这样的事情:
osx和linux之间存在巨大差异。 osx基于Unix,以linux以外的其他方式处理多任务处理。
Unix安装需要严格且定义良好的硬件机制,并且只能在特定的CPU机器上运行,并且osx可能不是为加速python进程而设计的。这个原因可能是原因。
有关详细信息,请参阅MultiProcessing文档。
我希望它有所帮助。