OSX和Linux之间使用Python多处理进行通信的性能差异

时间:2017-12-19 19:47:25

标签: python linux macos multiprocessing communication

我一直在努力了解有关Python multiprocessing模块的更多信息,并评估流程之间通信的不同技术。我编写了一个基准测试,用于比较PipeQueueArray(来自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之间的性能差异,这似乎更令人期待。

2 个答案:

答案 0 :(得分:4)

TL; DR:OSX使用Array更快,因为在Linux上对C库的调用会减慢Array的速度

使用Array中的multiprocessing使用C types Python library进行C调用以设置数组的内存。在Linux上比在OSX上花费更多的时间。您也可以使用pypy在OSX上观察到这一点。使用pypy(以及GCC和LLVM)设置内存所需的时间比在OSX(使用Clang)上使用python3的时间更长。

TL; DR:Windows和OSX之间的区别在于多处理启动新进程的方式

主要区别在于multiprocessing的实现,它在OSX下的工作方式不同于Windows。最重要的区别是multiprocessing启动新进程的方式。可以通过三种方式完成此操作:使用spawnforkforkserver。 Windows下的默认(且仅受支持的)方式为spawn。 * nix(包括OSX)下的默认方式是forkmultiprocessing文档的Contexts and start methods部分对此进行了记录。

导致结果偏差的另一个原因是迭代次数少。

如果增加迭代次数并计算每个时间单位的已处理函数调用次数,则这三种方法之间将获得相对一致的结果。

进一步分析:查看使用cProfile进行的函数调用

我删除了您的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与Array的区别

我看到的是队列比管道快,比管道快。无论使用哪种平台(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中的RawArraysharedctypes.py。这些函数仅在Array场景中调用(不在Pipe或Queue中)。在Linux上,这些调用几乎花费70%的时间,而在OSX上仅花费42%的时间。所以这是一个主要因素。

如果放大to the code,我们会看到Array(第84行)调用RawArray,而RawArray(第54行)除了调用ctypes.memsetdocumentation)。因此,我们有一个嫌疑犯。让我们测试一下。

以下代码使用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性能差异

接下来,我在OSX和Windows下的双引导MacBook Pro上运行此程序。优点是底层硬件相同。只有操作系统不同。我将迭代次数增加到1000,并将大小增加到10.000。

结果如下:

  • OSX:
    • 数组:10.895秒内有225668个呼叫
    • 管道:209552在6.894秒内通话
    • 队列:在7.892秒内致电728173
  • Windows:
    • 数组:354076在296.050秒内致电
    • 管道:在234.996秒内拨打374229
    • 队列:在250.966秒内致电903705

我们可以看到:

  1. 与使用OSX(使用spawn)相比,Windows实现(使用fork)进行的调用更多;
  2. Windows实施每次调用比OSX花费更多的时间。

虽然不是立即显而易见,但需要注意的是,如果查看每个调用的平均时间,则三种多重处理方法(数组,队列和管道)之间的相对模式是相同的(参见下图)。换句话说: OSX和Windows中的Array,Queue和Pipe之间的性能差异可以由两个因素完全解释:1.两个平台之间的Python性能差异; 2.两种平台处理多重处理的方式不同。

换句话说:multiprocessing文档的Contexts and start methods部分解释了呼叫数量的差异。 OSX和Windows之间的Python性能差异解释了执行时间的差异。如果剔除这两个组件,则如下图所示,在OSX和Windows上,Array,Queue和Pipe的相对性能(或多或少)是可比的。

Performance differences of Array, Queue and Pipe between OSX and Windows

答案 1 :(得分:-3)

好吧,当我们谈论python的多进程时,会发生这样的事情:

  • 操作系统完成所有多任务处理工作
  • 多核并发的唯一选择
  • 重复使用系统资源

osx和linux之间存在巨大差异。 osx基于Unix,以linux以外的其他方式处理多任务处理。

Unix安装需要严格且定义良好的硬件机制,并且只能在特定的CPU机器上运行,并且osx可能不是为加速python进程而设计的。这个原因可能是原因。

有关详细信息,请参阅MultiProcessing文档。

我希望它有所帮助。