繁体   English   中英

在将结果附加到字典的 for 循环上使用 python 多处理

[英]Using python multiprocessing on a for loop that appends results to dictionary

因此,我查看了多处理模块的文档以及此处提出的其他问题,似乎没有一个与我的情况相似,因此我开始了一个新问题。

为简单起见,我有一段如下形式的代码:

# simple dataframe of some users and their properties.
data = {'userId': [1, 2, 3, 4],
        'property': [12, 11, 13, 43]}
df = pd.DataFrame.from_dict(data)

# a function that generates permutations of the above users, in the form of a list of lists
# such as [[1,2,3,4], [2,1,3,4], [2,3,4,1], [2,4,1,3]]
user_perm = generate_permutations(nr_perm=4)

# a function that computes some relation between users
def comp_rel(df, permutation, user_dict):
    df1 = df.userId.isin(permutation[0])
    df2 = df.userId.isin(permutation[1])
    user_dict[permutation[0]] += permutation[1]
    return user_dict


# and finally a loop: 
user_dict = defaultdict(int)
for permutation in user_perm:
    user_dict = comp_rel(df, permutation, user_dict)    

我知道这段代码现在没有什么意义(如果有的话),但我只是写了一个小例子,它接近我正在处理的实际代码的结构。 user_dict应该终于包含userIds和一定的价值。

我有实际的代码,它工作正常,给出了正确的字典和一切,但是......它在单个线程上运行。 它非常缓慢,请记住我还有另外 15 个完全免费的线程。

我的问题是,如何使用 python 的multiprocessing模块来更改最后一个 for 循环,并能够在所有可用线程/内核上运行? 我看了文档,不是很容易理解。

编辑:我正在尝试将池用作:

p = multiprocessing.Pool(multiprocessing.cpu_count())
p.map(comp_rel(df, permutation, user_dict), user_perm)
p.close()
p.join()

但是这会中断,因为我正在使用该行:

user_dict = comp_rel(df, permutation, user_dict) 

在初始代码中,我不知道在池完成后应该如何合并这些字典。

您的comp_rel有两部分需要分开 - 首先是计算本身,它正在为某些用户 ID 计算一些值。 第二个是“累积”步骤,该步骤将该值添加到user_dict结果中。

你可以计算本身分离,使得它返回的元组(id, value)和多处理外包出去,然后在主线程之后积累的结果:

from multiprocessing import Pool
from functools import partial
from collections import defaultdict

# We make this a pure function that just returns a result instead of mutating anything
def comp_rel(df, perm):
    ...
    return perm[0], perm[1]

comp_with_df = partial(comp_rel, df) # df is always the same, so factor it out
with Pool(None) as pool: # Pool(None) uses cpu_count automatically
    results = pool.map(comp_with_df, user_perm)

# Now add up the results at the end:
user_dict = defaultdict(int)
for k, v in results:
    user_dict[k] += v

或者,您也可以直接将Manager().dict()对象传递到处理函数中,但这有点复杂,并且可能不会给您带来任何额外的速度。


根据@Masklinn 的建议,这里有一个更好的方法来避免内存开销:

user_dict = defaultdict(int)
with Pool(None) as pool:
    for k, v in pool.imap_unordered(comp_with_df, user_perm):
        user_dict[k] += v

通过这种方式,我们将完成的结果相加,而不必先将它们全部存储在中间列表中。

在评论中进行简短讨论后,我决定使用ProcessPoolExecutor发布解决方案:

import concurrent.futures
from collections import defaultdict

def comp_rel(df, perm):
    ...
    return perm[0], perm[1]

user_dict = defaultdict(int)
with concurrent.futures.ProcessPoolExecutor() as executor:
    futures = {executor.submit(comp_rel, df, perm): perm for perm in user_perm}
    for future in concurrent.futures.as_completed(futures):
        try:
            k, v = future.result()
        except Exception as e:
            print(f"{futures[future]} throws {e}")
        else:
            user_dict[k] += v

它的工作原理与@tzaman 相同,但它为您提供了处理异常的可能性。 此模块中还有更多有趣的功能,请查看docs

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM