我如何并行化一个简单的Python循环?

这可能是一个微不足道的问题,但我如何在python中并行化下面的循环?

# setup output lists
output1 = list()
output2 = list()
output3 = list()

for j in range(0, 10):
    # calc individual parameter value
    parameter = j * offset
    # call the calculation
    out1, out2, out3 = calc_stuff(parameter = parameter)

    # put results into correct output list
    output1.append(out1)
    output2.append(out2)
    output3.append(out3)

我知道如何在Python中启动单线程,但我不知道如何“收集”结果。

多个进程也可以,无论这种情况最简单。 我目前使用Linux,但代码应该在Windows和Mac上运行。

并行化这些代码的最简单方法是什么?


由于全局解释器锁定(GIL),在CPython上使用多个线程不会为纯Python代码提供更好的性能。 我建议使用multiprocessing模块:

pool = multiprocessing.Pool(4)
out1, out2, out3 = zip(*pool.map(calc_stuff, range(0, 10 * offset, offset)))

请注意,这在交互式解释器中不起作用。

为了避免GIL周围的通常的FUD:无论如何,对于这个示例使用线程没有任何优势。 你想在这里使用进程,而不是线程,因为它们避免了一堆问题。


为了并行化一个简单的for循环,joblib为多处理的原始使用带来了很大的价值。 不仅是简短的语法,而且还包括当迭代速度非常快(消除开销)或捕获子进程回溯时的透明迭代,以获得更好的错误报告。

免责声明:我是joblib的原作者。


并行化这些代码的最简单方法是什么?

我真的很喜欢concurrent.futures自3.2版本对于这一点,在Python3可用-并通过反向移植到2.6和2.7 PyPI上。

您可以使用线程或进程并使用完全相同的界面。

把它放在一个文件中 - futuretest.py:

import concurrent.futures
import time, random               # add some random sleep time

offset = 2                        # you don't supply these so
def calc_stuff(parameter=None):   # these are examples.
    sleep_time = random.choice([0, 1, 2, 3, 4, 5])
    time.sleep(sleep_time)
    return parameter / 2, sleep_time, parameter * parameter

def procedure(j):                 # just factoring out the
    parameter = j * offset        # procedure
    # call the calculation
    return calc_stuff(parameter=parameter)

def main():
    output1 = list()
    output2 = list()
    output3 = list()
    start = time.time()           # let's see how long this takes

    # we can swap out ProcessPoolExecutor for ThreadPoolExecutor
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for out1, out2, out3 in executor.map(procedure, range(0, 10)):
            # put results into correct output list
            output1.append(out1)
            output2.append(out2)
            output3.append(out3)
    finish = time.time()
    # these kinds of format strings are only available on Python 3.6:
    # time to upgrade!
    print(f'original inputs: {repr(output1)}')
    print(f'total time to execute {sum(output2)} = sum({repr(output2)})')
    print(f'time saved by parallelizing: {sum(output2) - (finish-start)}')
    print(f'returned in order given: {repr(output3)}')

if __name__ == '__main__':
    main()

这里是输出:

$ python3 -m futuretest
original inputs: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
total time to execute 33 = sum([0, 3, 3, 4, 3, 5, 1, 5, 5, 4])
time saved by parallellizing: 27.68999981880188
returned in order given: [0, 4, 16, 36, 64, 100, 144, 196, 256, 324]

多线程

现在将ProcessPoolExecutor更改为ThreadPoolExecutor ,然后再次运行该模块:

$ python3 -m futuretest
original inputs: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
total time to execute 19 = sum([0, 2, 3, 5, 2, 0, 0, 3, 3, 1])
time saved by parallellizing: 13.992000102996826
returned in order given: [0, 4, 16, 36, 64, 100, 144, 196, 256, 324]

现在你已经完成了多线程和多处理!

关于性能和使用两者的注意事项。

抽样太小而无法比较结果。

但是,我怀疑多线程将比一般的多处理更快,特别是在Windows上,因为Windows不支持分叉,因此每个新进程都需要一段时间才能启动。 在Linux或Mac上,它们可能会更接近。

您可以在多个进程中嵌套多个线程,但建议不要使用多个线程来分离多个进程。

链接地址: http://www.djcxy.com/p/79379.html

上一篇: How do I parallelize a simple Python loop?

下一篇: Can't understand python shallow copy when working with int and str