python中的多处理以加速函数

python中的多处理以加速函数,python,windows,map,multiprocessing,Python,Windows,Map,Multiprocessing,我对Python多处理感到困惑 我试图加快处理数据库字符串的函数的速度,但我一定误解了多处理的工作原理,因为将该函数提供给一个工作池比“正常处理”花费的时间更长 这里有一个我正在努力实现的例子 from time import clock, time from multiprocessing import Pool, freeze_support from random import choice def foo(x): TupWerteMany = [] for i in

我对Python多处理感到困惑

我试图加快处理数据库字符串的函数的速度,但我一定误解了多处理的工作原理,因为将该函数提供给一个工作池比“正常处理”花费的时间更长

这里有一个我正在努力实现的例子

from time import clock, time
from multiprocessing import Pool, freeze_support

from random import choice


def foo(x):
    TupWerteMany = []
    for i in range(0,len(x)):
         TupWerte = []
          s = list(x[i][3])
          NewValue = choice(s)+choice(s)+choice(s)+choice(s)
          TupWerte.append(NewValue)
          TupWerte = tuple(TupWerte)

          TupWerteMany.append(TupWerte)
     return TupWerteMany



 if __name__ == '__main__':
     start_time = time()
     List = [(u'1', u'aa', u'Jacob', u'Emily'),
        (u'2', u'bb', u'Ethan', u'Kayla')]
     List1 = List*1000000

     # METHOD 1 : NORMAL (takes 20 seconds) 
     x2 = foo(List1)
     print x2[1:3]

     # METHOD 2 : APPLY_ASYNC (takes 28 seconds)
     #    pool = Pool(4)
     #    Werte = pool.apply_async(foo, args=(List1,))
     #    x2 = Werte.get()
     #    print '--------'
     #    print x2[1:3]
     #    print '--------'

     # METHOD 3: MAP (!! DOES NOT WORK !!)

     #    pool = Pool(4)
     #    Werte = pool.map(foo, args=(List1,))
     #    x2 = Werte.get()
     #    print '--------'
     #    print x2[1:3]
     #    print '--------'


     print 'Time Elaspse: ', time() - start_time
我的问题是:

  • 为什么apply_async需要比“正常方式”更长的时间
  • 我把地图弄错了什么
  • 使用多处理加速此类任务是否有意义
  • 最后:在我读完这里之后,我想知道python中的多处理是否在windows上工作
    因此,您的第一个问题是在
    foo(x)
    中没有实际的并行性,您只需将整个列表传递给函数一次

    (一) 进程池的概念是让许多进程在某些数据的独立位上进行计算

     # METHOD 2 : APPLY_ASYNC
     jobs = 4
     size = len(List1)
     pool = Pool(4)
     results = []
     # split the list into 4 equally sized chunks and submit those to the pool
     heads = range(size/jobs, size, size/jobs) + [size]
     tails = range(0,size,size/jobs)
     for tail,head in zip(tails, heads):
          werte = pool.apply_async(foo, args=(List1[tail:head],))
          results.append(werte)
    
     pool.close()
     pool.join() # wait for the pool to be done
    
     for result in results:
          werte = result.get() # get the return value from the sub jobs
    
    如果处理每个块所需的时间大于启动流程所需的时间,那么这只会给您带来实际的加速,在有四个流程和四个作业要完成的情况下,当然,如果您有4个流程和100个作业要完成,这些动态会发生变化。请记住,您四次创建一个全新的python解释器,这不是免费的

    2) map的问题是,它在一个单独的过程中将
    foo
    应用于
    List1
    中的每个元素,这将花费相当长的时间。因此,如果您的池有4个进程
    map
    将弹出列表中的一项四次,并将其发送给要处理的进程-等待进程完成-弹出列表中的更多内容-等待进程完成。只有当处理单个项目需要很长时间时,这才有意义,例如,如果每个项目都是指向1GB文本文件的文件名。但目前,map只需要获取列表的单个字符串,并将其传递给
    foo
    ,其中as
    apply\u async
    获取列表的一部分。请尝试以下代码

    def foo(thing):
        print thing
    
    map(foo, ['a','b','c','d'])
    
    这是内置的python映射,将运行单个进程,但多进程版本的想法完全相同

    根据J.F.Sebastian的评论添加:但是,您可以使用
    chunksize
    参数来
    map
    为每个块指定大约的大小

    pool.map(foo, List1, chunksize=size/jobs) 
    
    不过我不知道Windows上的
    map
    是否有问题,因为我没有可供测试的映射

    3) 是的,考虑到您的问题已经足够大,有理由推出新的python解释器


    4) 不能给你一个明确的答案,因为这取决于内核/处理器的数量等。但一般来说,在Windows上应该可以。如果你感兴趣,这里有一个通用的多处理模板

    import multiprocessing as mp
    import time
    
    def worker(x):
        time.sleep(0.2)
        print "x= %s, x squared = %s" % (x, x*x)
        return x*x
    
    def apply_async():
        pool = mp.Pool()
        for i in range(100):
            pool.apply_async(worker, args = (i, ))
        pool.close()
        pool.join()
    
    if __name__ == '__main__':
        apply_async()
    
    输出如下所示:

    x= 0, x squared = 0
    x= 1, x squared = 1
    x= 2, x squared = 4
    x= 3, x squared = 9
    x= 4, x squared = 16
    x= 6, x squared = 36
    x= 5, x squared = 25
    x= 7, x squared = 49
    x= 8, x squared = 64
    x= 10, x squared = 100
    x= 11, x squared = 121
    x= 9, x squared = 81
    x= 12, x squared = 144
    
    正如您所看到的,这些数字不是按顺序排列的,因为它们是异步执行的。

    关于问题(2) 在Dougal和Matti的指导下,我找到了问题所在。 原始的foo函数处理列表列表,而map需要一个函数来处理单个元素

    新功能应该是

    def foo2 (x):
        TupWerte = []
        s = list(x[3])
        NewValue = choice(s)+choice(s)+choice(s)+choice(s)
        TupWerte.append(NewValue)
        TupWerte = tuple(TupWerte)
        return TupWerte
    
    以及称之为: 感谢所有帮助我理解这一点的人

    所有方法的结果:
    对于List*2 Mio记录:正常13.3秒,与异步并行7.5秒,与chunksize:7.3的map并行,不带chunksize 5.2秒(1)已被多次提及,简短的故事是并发并不是一种神奇的更快的方法,它有开销,还受到处理器数量和操作系统可以释放多少资源的限制。我想去搜索一个链接,但是这些东西很难搜索。谢谢Delnan。请相信我在提出问题之前已经进行了广泛的搜索。我理解你的观点,但希望有人能对这一特殊情况有一个“具体”的答案。@user1043144:当你改变池中进程的数量时,时间是如何变化的?实际上没有区别。我一定是在什么地方弄错了。你的
    地图不起作用的原因是你的论点错了;没有
    args
    参数。它应该是
    pool.map(foo,List1)
    (就像bultin
    map
    ),但是
    foo
    实际上需要一个列表。您应该将
    foo
    更改为仅内部循环部分,或者执行类似于
    pool.map(foo,(对于xrange中的i(len(List1),None,None,10))的操作
    。此外,对于成本不高的较小的sof操作集,设置多处理功能的开销比您获得的任何加速都要大。这只是我创建的一个示例。我的实际问题涉及大量数据集。
    pool.map
    不会每次启动一个新进程;它会重用这四个进程(或多个)由
    池创建的
    。但是,它每次都会对参数进行pickle/unpickle处理。我从来没有说过它会启动新的process@user1043144如果你的意思是“uuuGe”,你可能想考虑不要在主进程中加载数据,但是在<代码> fo中,你可以使用<代码> MMAP>代码>只加载文件的那一部分。这实际上是需要的。加载和传递所有这些数据也会让你付出代价,因此,根据数据量的大小,可能也会有一些显著的节省。这与问题有什么关系?文档中也可以找到一个通用的
    mp
    模板?b/c他们甚至没有以正确的方式使用多处理问:这是一个正确的方法,海报可以使用它来正确地重写它们的功能。
    jobs = 4
    size = len(List1)
    pool = Pool()
    #Werte = pool.map(foo2, List1, chunksize=size/jobs)
    Werte = pool.map(foo2, List1)
    pool.close()
    print Werte[1:3]