dda*_*lla 26 python linux macos communication multiprocessing
我一直在尝试更多地了解Python的multiprocessing模块,并评估不同的流程之间的通信技术.我写的比较的性能的基准Pipe,Queue和Array(都来自multiprocessing)用于传递numpy进程之间的阵列.完整的基准可以在这里找到.以下是测试的片段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
Run Code Online (Sandbox Code Playgroud)
我在我的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
Run Code Online (Sandbox Code Playgroud)
Array由于它使用共享内存并且可能不需要酸洗,我有点惊讶地看到表现如此糟糕,但我认为必须有一些numpy我无法控制的复制.
但是,我在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
Run Code Online (Sandbox Code Playgroud)
真正的时间差异并不令人惊讶,因为当然不同的系统会表现出不同的性能.什么是如此令人惊讶的是在相对时间的差异.
有什么可以解释这个?这对我来说是一个非常令人惊讶的结果.看到Linux和Windows,OSX和Windows之间存在如此明显的差异,我不会感到惊讶,但我有点认为这些东西在OSX和Linux之间的表现非常相似.
使用Arrayfrommultiprocessing使用C 类型 Python 库进行 C 调用以设置数组的内存。这在 Linux 上比在 OSX 上花费的时间相对更多。您还可以使用 pypy 在 OSX 上观察这一点。使用 pypy(以及 GCC 和 LLVM)设置内存比在 OSX 上使用 python3(使用 Clang)花费的时间要长得多。
主要区别在于 的实现multiprocessing,它在 OSX 和 Windows 下的工作方式不同。最重要的区别是multiprocessing启动新流程的方式。可以通过三种方式完成此操作:使用spawn,fork或forkserver。Windows 下的默认(且仅受支持)方式是spawn. *nix(包括 OSX)下的默认方式是fork. 这记录在文档的上下文和启动方法部分multiprocessing。
结果偏差的另一个原因是您采用的迭代次数较少。
如果增加迭代次数并计算每个时间单位处理的函数调用次数,则三种方法之间会得到相对一致的结果。
我删除了您的timeit计时器功能并将您的代码包装在cProfile分析器中。
我添加了这个包装函数:
def run_test(iters, size, func):
for _ in range(iters):
func(size)
Run Code Online (Sandbox Code Playgroud)
我用以下方法替换了循环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()
Run Code Online (Sandbox Code Playgroud)
我看到的是队列比管道快,管道比数组快。无论平台(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和RawArrayin sharedctypes.py. 这些函数仅在 Array 场景中调用(不在 Pipe 或 Queue 中)。在 Linux 上,这些调用占用了近 70% 的时间,而在 OSX 上只占用了 42% 的时间。所以这是一个主要因素。
如果我们放大代码,我们会看到Array(第 84 行)调用RawArray和RawArray(第 54 行)除了调用ctypes.memset(文档)之外没有任何特别之处。所以我们有一个嫌疑人。让我们测试一下。
以下代码使用 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)
Run Code Online (Sandbox Code Playgroud)
在我的 MacBookPro 和我的 Linux 服务器上运行它证实了它在 Linux 上的运行速度比在 OSX 上慢得多的行为。知道pypy在使用GCC和 Apples LLVM编译的 OSX 上,这比 Python 更类似于 Linux 世界,Python 在 OSX 上直接针对Clang编译。通常,Python 程序在 pypy 上的运行速度比在 CPython 上快,但上面的代码在 pypy 上的运行速度要慢 6.4 倍(在相同的硬件上!)。
我对 C 工具链和 C 库的了解有限,所以我无法深入挖掘。所以我的结论是:OSX 和 Windows 使用 Array 更快,因为在 Linux 上对 C 库的内存调用会减慢 Array 的速度。
接下来,我在 OSX 和 Windows 下的双启动 MacBook Pro 上运行它。优点是底层硬件相同;只有操作系统不同。我将迭代次数增加到 1000,大小增加到 10.000。
结果如下:
我们可以看到:
spawn)比 OSX(使用fork)需要更多的调用;不是很明显,但需要注意的是,如果您查看每次调用的平均时间,三种多处理方法(数组、队列和管道)之间的相对模式是相同的(见下图)。换句话说:OSX 和 Windows 中 Array、Queue 和 Pipe 的性能差异完全可以用两个因素来解释:1. 两个平台之间 Python 性能的差异;2. 两个平台处理多处理的不同方式。
换句话说:文档的上下文和启动方法部分解释了调用次数的差异multiprocessing。OSX 和 Windows 之间 Python 的性能差异解释了执行时间的差异。如果去掉这两个组件,数组、队列和管道的相对性能在 OSX 和 Windows 上(或多或少)相当,如下图所示。
| 归档时间: |
|
| 查看次数: |
1009 次 |
| 最近记录: |