python 中无法 pickle 的多进程任务?

Multiprocess tasks in python that can't be pickled?

我正在尝试在 Mac OSX 上使用 python (2.7.8) 中的多处理。在阅读 Velimir Mlaker 对此 question 的回答后,我能够使用 multiprocessing.Pool() 对一个非常简单的函数进行多处理,但它不适用于我的实际函数。我得到了正确的结果,但它是串行执行的。我认为问题在于我的函数循环遍历 music21.stream() ,它类似于列表但具有音乐数据的特殊功能。我相信 music21 流不能被腌制,所以我可以使用一些多处理替代池吗?我不介意返回的结果是否乱序,如有必要,我可以升级到不同版本的 python。我已经包含了我的多处理任务代码,但没有包含它调用的 stream_indexer() 函数的代码。谢谢!

import multiprocessing as mp
def basik(test_piece, part_numbers):
    jobs = []
    for i in part_numbers:
        # Each 2-tuple in jobs has an index <i> and a music21 stream that
        # corresponds to an individual part in a musical score.
        jobs.append((i, test_piece.parts[i]))
    pool = mp.Pool(processes=4)
    results = pool.map(stream_indexer, jobs)
    pool.close()
    pool.join()

    return results

music21 的最新 git 提交具有基于 joblib 的功能,可帮助解决多处理中一些比较棘手的部分。例如,如果你想计算一个部分中的所有音符,你通常可以连续做:

import music21
def countNotes(s):
    return len(s.recurse().notes)
    # using recurse() instead of .flat to avoid certain caches...

bach = music21.corpus.parse('bach/bwv66.6')
[countNotes(p) for p in bach.parts]

并行它是这样工作的:

music21.common.runParallel(list(bach.parts), countNotes)

但是!这是一个巨大的警告。让我们计时:

In [5]: %timeit music21.common.runParallel(list(b.parts), countNotes)
10 loops, best of 3: 152 ms per loop

In [6]: %timeit [countNotes(p) for p in b.parts]
100 loops, best of 3: 2.19 ms per loop

在我的计算机上(2 核,4 线程),运行ning 并行比串行 运行ning 慢近 100 倍。为什么?因为准备要进行多处理的 Stream 的开销很大。如果 运行 的例程非常慢(大约 1 毫秒/音符除以处理器数量),那么值得在多处理中传递 Streams。否则,看看是否有办法只来回传递少量信息,例如处理路径:

def parseCountNotes(fn):
    s = corpus.parse(fn)
    return len(s.recurse().notes)  

bach40 = [b.sourcePath for b in music21.corpus.search('bwv')[0:40]]

In [32]: %timeit [parseCountNotes(b) for b in bach40]
1 loops, best of 3: 2.39 s per loop

In [33]: %timeit music21.common.runParallel(bach40, parseCountNotes)
1 loops, best of 3: 1.83 s per loop

在这里,即使在 MacBook Air 上,我们也开始获得加速。在我的办公室 Mac Pro 上,对于这样的调用,加速会变得很大。在这种情况下,对 parse 的调用在对 recurse().

的时间内占主导地位