在 Python (Windows) 中使用池进行多处理

Using pool for multiprocessing in Python (Windows)

我必须以并行方式进行学习才能运行更快。我是 python 中多处理库的新手,还无法成功 运行。 在这里,我正在调查是否每对 (origin, target) 都保留在我研究的不同框架之间的特定位置。几点:

  1. 这是一个功能,我想运行更快(不是几个进程)。
  2. 后续处理;这意味着每一帧都与前一帧进行比较。
  3. 此代码是原始代码的一种非常简单的形式。代码输出 residece_list.
  4. 我正在使用 Windows OS.

有人可以检查代码(多处理部分)并帮助我改进它以使其工作。谢谢

import numpy as np
from multiprocessing import Pool, freeze_support


def Main_Residence(total_frames, origin_list, target_list):
    Previous_List = {}
    residence_list = []

    for frame in range(total_frames):     #Each frame

        Current_List = {}               #Dict of pair and their residence for frames
        for origin in range(origin_list):

            for target in range(target_list):
                Pair = (origin, target)         #Eahc pair

                if Pair in Current_List.keys():     #If already considered, continue
                    continue
                else:
                    if origin == target:
                        if (Pair in Previous_List.keys()):            #If remained from the previous frame, add residence
                            print "Origin_Target remained: ", Pair
                            Current_List[Pair] = (Previous_List[Pair] + 1)
                        else:                                           #If new, add it to the current
                            Current_List[Pair] = 1

        for pair in Previous_List.keys():                        #Add those that exited from residence to the list
            if pair not in Current_List.keys():
                residence_list.append(Previous_List[pair])

        Previous_List = Current_List
    return residence_list

if __name__ == '__main__':
    pool = Pool(processes=5)
    Residence_List = pool.apply_async(Main_Residence, args=(20, 50, 50))
    print Residence_List.get(timeout=1)
    pool.close()
    pool.join()
    freeze_support()

Residence_List = np.array(Residence_List) * 5

多处理在您此处介绍的上下文中没有意义。 您正在创建五个子进程(以及三个属于池的线程,管理工作人员、任务和结果)来执行 one 函数 once。所有这些都是以系统资源和执行时间为代价的,而您的四个工作进程根本不做任何事情。多处理不会加速函数的执行。您的具体示例中的代码总是比在主进程中直接执行 Main_Residence(20, 50, 50) 慢。

为了使多处理在这种情况下有意义,您手头的工作需要分解为一组可以并行处理的同质任务,它们的结果可能会在以后合并。

举个例子(不一定是好例子),如果你想计算一个数字序列的最大质因数,你可以将计算任何特定数字的那个因数的任务委托给池中的工人.然后几个工作人员将并行进行这些单独的计算:

def largest_prime_factor(n):
    p = n
    i = 2
    while i * i <= n:
        if n % i:
            i += 1
        else:
            n //= i
    return p, n


if __name__ == '__main__':
    pool = Pool(processes=3)
    start = datetime.now()
    # this delegates half a million individual tasks to the pool, i.e. 
    # largest_prime_factor(0), largest_prime_factor(1), ..., largest_prime_factor(499999)      
    pool.map(largest_prime_factor, range(500000))
    pool.close()
    pool.join()
    print "pool elapsed", datetime.now() - start
    start = datetime.now()
    # same work just in the main process
    [largest_prime_factor(i) for i in range(500000)]
    print "single elapsed", datetime.now() - start

输出:

pool elapsed 0:00:04.664000
single elapsed 0:00:08.939000

largest_prime_factor 函数取自 @Stefan in this answer

如您所见,池仅比执行相同工作量的单进程执行速度大约快两倍,而 运行 三个进程并行执行。这是由于 multiprocessing/the 池引入的开销。

因此,您声明示例中的代码已被简化。您必须分析您的原始代码,看看它是否可以分解为可以传递到您的池进行处理的同类任务。如果可能的话,使用多处理可能会帮助您加快程序速度。否则,多处理可能会浪费您的时间,而不是节省时间。

编辑:
由于您要求对代码提出建议。我几乎不能说你的功能。你自己说这只是一个提供 MCVE 的简化示例(顺便说一句,非常感谢!大多数人不会花时间将他们的代码精简到最低限度)。无论如何,代码审查请求更适合 Codereview

尝试一下可用的任务委派方法。在我的质因数示例中,使用 apply_async 会带来 巨大的 惩罚。与使用 map 相比,执行时间增加了九倍。但是我的例子只使用了一个简单的迭代器,你的每个任务需要三个参数。这可能是 starmap 的情况,但仅适用于 Python 3.3。
无论如何,任务数据的 structure/nature 基本上决定了正确的使用方法.
我用 multiprocessing 你的示例函数做了一些 q&d 测试。 输入定义如下:

inp = [(20, 50, 50)] * 5000  # that makes 5000 tasks against your Main_Residence

我 运行 在 Python 3.6 中的三个子流程中,您的功能保持不变,除了删除 print 语句(I/O 是昂贵的)。我使用 starmap, apply, starmap_async and apply_async 并且每次都迭代结果以解决异步结果上的阻塞 get()
这是输出:

starmap elapsed 0:01:14.506600
apply elapsed 0:02:11.290600
starmap async elapsed 0:01:27.718800
apply async elapsed 0:01:12.571200
# btw: 5k calls to Main_Residence in the main process looks as bad 
# as using apply for delegation
single elapsed 0:02:12.476800

如您所见,尽管所有四种方法所做的工作量相同,但执行时间不同;您选择的 apply_async 似乎是最快的方法。

编码风格。您的代码看起来很...非常规 :) 您使用 Capitalized_Words_With_Underscore 作为您的名称(函数名和变量名),这在 Python 中几乎是禁忌。此外,将名称 Previous_List 分配给字典是......有问题的。查看 PEP 8, especially the section Naming Conventions 以了解 Python.

普遍接受的编码风格

从您的 print 外观来看,您仍在使用 Python 2. 我知道在公司或机构环境中,有时您只能使用它。不过,请记住 clock for Python 2 is ticking