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

时间:2019-03-05 06:07:01

标签: python numpy multiprocessing pool

我必须以并行方式进行研究,以使其运行更快。我是python中的多处理库的新手,还无法使其成功运行。 在这里,我正在调查每对(来源,目标)是否仍留在我研究的各个框架之间的特定位置。几点:

  1. 这是一个功能,我想运行得更快(不是几个进程)。
  2. 随后执行该过程;这意味着每一帧都与前一帧进行比较。
  3. 此代码是原始代码的非常简单的形式。该代码将输出一个residentce_list。
  4. 我正在使用Windows操作系统。

有人可以检查代码(“多处理”部分)并帮助我对其进行改进以使其正常工作。谢谢。

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

1 个答案:

答案 0 :(得分:3)

在您在此处呈现的上下文中,多处理没有意义。 您正在创建五个子流程(以及三个属于该池的线程,分别管理工作程序,任务和结果)来执行一次 函数 。所有这些都是在系统资源和执行时间上付出代价的,而您的四个工作进程根本什么都不做。多重处理不会加快功能的执行。您特定示例中的代码总是比在主流程中简单执行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中的this answer

如您所见,在并行运行三个进程的同时,该池仅是执行相同工作量的单个进程执行速度的大约两倍。这是由于多处理/池带来的开销。

因此,您表示示例中的代码已简化。您必须分析您的原始代码,以查看是否可以将其分解为可传递给池进行处理的同质任务。如果可能的话,使用多重处理可能会帮助您加快程序速度。如果没有,那么多处理可能会花费您时间,而不是节省时间。

修改:
由于您要求有关代码的建议。关于您的功能,我几乎无话可说。您自己说过,提供MCVE只是一个简化的示例(顺便说一句,不胜感激!大多数人不花时间将其代码精简到最低限度)。无论如何,Codereview都更适合进行代码审查的请求。

尝试一些可用的任务委托方法。在我的主要因素示例中,使用apply_async带来了 mass 惩罚。与使用map相比,执行时间增加了9倍。但我的示例仅使用一个简单的迭代器,每个任务需要三个参数。 starmap可能是这种情况,但仅从Python 3.3开始可用。
无论如何,任务数据的结构/性质基本上决定了要使用的正确方法。
我对示例函数进行了多处理,从而进行了一些问答测试。 输入的定义如下:

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

我在Python 3.6中以三个子进程运行了该函数,除了删除了print语句(I / O成本很高)之外,您的函数未更改。我使用了starmapapplystarmap_asyncapply_async,并且每次都遍历结果以解决对异步结果的阻塞get()。 br /> 输出如下:

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,尤其是Naming Conventions部分,了解Python普遍接受的编码风格。

print的外观来看,您仍在使用Python2。我知道在公司或机构环境中,有时候您已经可以使用所有的东西。不过,请记住clock for Python 2 is ticking