Python,在并行循环

时间:2017-04-04 07:29:36

标签: python numpy dictionary joblib

我编写了一些代码来并行执行一些计算(joblib)并使用计算结果更新字典。该代码由一个调用生成器函数的主函数和一个并行运行的计算函数组成。计算结果(键:值对)由计算函数的每个实例添加到在主函数和市场中创建的字典作为全局。

以下是我的代码的简化版本,说明了上述过程。

当一切都运行时,结果字典(d_result)为空,但应该已经填充了计算函数生成的结果。为什么会这样?

import numpy as np
from joblib import Parallel, delayed


def do_calc(d, r, pair_index): # function to be run in parallel

    data_1 = d[str(r)][pair_index, 1]
    data_2 = d[str(r)][pair_index, 2]
    result_name = str(data_1) + " ^ " + str(data_2)
    result = data_1 ** data_2
    d_result[result_name] = result
    # d_result.setdefault(result_name, []).append(result)  ## same result as above


def compute_indices(d): # generator function

    for r in d:
        num_pairs = d[str(r)].shape[0]
        for pair_index in range(num_pairs):
            yield r, pair_index


def process(): # main function

    global d_result
    d_result = {}
    r1 = np.array([['ab', 1, 2], ['vw', 10, 12]], dtype=object)
    r2 = np.array([['ac', 1, 3], ['vx', 10, 13]], dtype=object)
    r3 = np.array([['ad', 1, 4], ['vy', 10, 14]], dtype=object)
    r4 = np.array([['ae', 1, 5], ['vz', 10, 15]], dtype=object)
    d = {'r1': r1, 'r2': r2, 'r3': r3, 'r4': r4}
    Parallel(n_jobs=4)(delayed(do_calc)(d, r, pair_index) for r, pair_index in (compute_indices)(d))
    print(d_result)


process()

2 个答案:

答案 0 :(得分:2)

我很高兴你让你的计划工作。但是我认为你忽略了一些重要的东西,如果你用你的例子作为大型程序的基础,你可能会遇到麻烦。

我扫描了joblib的文档,发现它是基于Python多处理模块构建的。所以multiprocessing programming guidelines适用。

起初我无法弄清楚为什么你的新程序成功运行而原始程序没有成功。这是原因(来自上面的链接):“请记住,如果在子进程中运行的代码尝试访问全局变量,那么它看到的值(如果有的话)可能与父进程中的值不同调用Process.start时的进程。“这是因为每个子进程至少在概念上都有自己的Python解释器副本。在每个子进程中,必须导入该进程使用的代码。如果该代码声明全局变量,那么这两个进程将具有这些全局变量的单独副本,即使它在您阅读代码时看起来不那样。因此,当原始程序的子进程将数据放入全局d_result时,它实际上是父进程中与d_result不同的对象。再次从文档中说:“确保新的Python解释器可以安全地导入主模块,而不会导致意外的副作用(例如启动新进程)。

例如,在运行Windows的情况下,以下模块将因RuntimeError而失败:

from multiprocessing import Process

def foo():
    print 'hello'

p = Process(target=foo)
p.start()

相反,应该使用if __name__ == '__main__'来保护程序的入口点。“

因此,在最后一行之前向您的程序(第二个版本)添加一行代码非常重要:

if __name__ == "__main__":
    process()

如果不这样做,可能会导致一些你不想花时间讨厌的错误。

答案 1 :(得分:0)

好的,我已经明白了。答案和下面的新代码:

do_calc()函数现在生成一个空字典,然后用一个键:值对填充它并返回字典。

默认情况下,process()中的并行位会创建一个从do_calc()返回的列表。因此,在并行化do_calc()之后,我最终得到的是一个dicts列表。

我真正想要的是单个词典,所以使用词典理解我将词典列表转换为词典,而wala,她一切都很好!

这有助于:python convert list of single key dictionaries into a single dictionary

import numpy as np
from joblib import Parallel, delayed


def do_calc(d, r, pair_index):  # calculation function to be run in parallel

    data_1 = d[str(r)][pair_index, 1]
    data_2 = d[str(r)][pair_index, 2]
    result_name = str(data_1) + " ^ " + str(data_2)
    result = data_1 ** data_2
    d_result = {}  # create empty dict
    d_result[result_name] = result  #add key:value pair to dict
    return d_result  # return dict


def compute_indices(d):  # generator function

    for r in d:
        num_pairs = d[str(r)].shape[0]
        for pair_index in range(num_pairs):
            yield r, pair_index


def process():  # main function

    r1 = np.array([['ab', 1, 2], ['vw', 10, 12]], dtype=object)
    r2 = np.array([['ac', 1, 3], ['vx', 10, 13]], dtype=object)
    r3 = np.array([['ad', 1, 4], ['vy', 10, 14]], dtype=object)
    r4 = np.array([['ae', 1, 5], ['vz', 10, 15]], dtype=object)
    d = {'r1': r1, 'r2': r2, 'r3': r3, 'r4': r4}
    # parallelised calc.  Each run returns dict, final output is list of dicts
    d_result = Parallel(n_jobs=4)(delayed(do_calc)(d, r, pair_index) for r, pair_index in (compute_indices)(d))
    # transform list of dicts to dict
    d_result = {k: v for x in d_result for k, v in x.items()}
    print(d_result)

process()