zoukankan      html  css  js  c++  java
  • 【python技巧系列】python的multiprocessing到底怎么用的问题

    众所周知,由于python(Cpython)的全局锁(GIL)问题存在,导致Thread也就是线程的并行并不可实现。 multiprocessing 模块采用多进程而不是多线程的方式实现并行,解决了GIL的问题,一定程度上使状况得到了缓解。

    然而,Multiprocess本身依然有一些功能上的瓶颈。其中一个重要的是:进程之间不能共享内存(线程间则可以共享内存)。这意味着在进程间交换数据的时候,需要把数据打包、传递,解包。在python的语境下就是:

    "pickle from main process to the subprocess;

    depickle from subprocess to an object in memory;

    pickle and return to the main process;

    depickle from main process and return to memory"

    (具体详见这个问题下的吐槽)

    因此, 在需要在进程间共享巨大的数据包的时候,多进程的表现还不如单进程。

    除此之外,当需要运行的程序本身不是计算密集型而是是IO密集型,多进程所增加的读写会抵消掉运算速度的增益;如果程序复杂度根本不需要用并行来解决,那么建立进程(池)的时间很可能比运行程序本身还要慢;另外,在进程池 multiprocessing.Pool(n) 的 n  的选择上,如果选择了多于当前CPU的核心数目的数字( multiprocessing.cpu_count() ),那么在进程之间切换的功夫会大大拉低效率。

    建立对线程和进程关系的直观印象,可参考这篇文章

    快速而完整地了解python的全局锁(GIL)问题,参考这篇不错的博客


    为了解 multiprocess 的使用,我做了一些测试,测试环境是4核的Macbook Air。如下:

    from multiprocessing import Process, Manager, Pool
     1 def f(l):
     2     l.reverse()
     3     return
     4     
     5 def main():
     6     l1 = [random.randrange(0, 100000, 1) for i in range(0, 100000)]
     7     l2 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 
     8     l3 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 
     9     l4 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 
    10     l5 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 
    11     l6 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 
    12     l7 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 
    13     s = time.time() 
    14     for l in [l1, l2, l3, l4, l5, l6, l7]:
    15         f(l)
    16     print "%s seconds" % (time.time() - s)
    17     s = time.time()
    18     map(f, [l1, l2, l3, l4, l5, l6, l7])
    19     print "%s seconds" % (time.time() - s)
    20     p = Pool(4)
    21     s = time.time() 
    22     p.map(f, [l1, l2, l3, l4, l5, l6, l7])
    23     print "%s seconds" % (time.time() - s)
    24     return

    也就是分别测试 f() 对 l1, l2, l3, l4, l5, l6, l7 7个列表的操作时间。先是循环的依次操作,再是python中非常好用的 map() 函数,最后是 multiprocessing 的进程池 multiprocessing.Pool.map() ——进程池中建立了4个 worker process , 也就是说,接下来的任务会被随机地分配给4个进程来完成。

    每次操作之前都重新计时,得到了这样的结果:

    >>> main()
    0.00250101089478 seconds
    0.000663995742798 seconds
    0.907639980316 seconds

    多进程出奇得慢。而 map() 相对于循环操作有很大的效率提升。

    所以并不是所有任务都适合多进程(至于列表的倒置为什么不适合多进程,我并不明白。。)。在最近的实验中,我需要找到提升效率的方法,以后的测试会陆续放上来。


    实验的时候,本来用 Pool(n) ,但是始终卡在了 racquire() 这里(ctrl-c终止后停在的地方),于是改用 Process() ,就可以运行了。但是运行到中间的某一行后,本来设定的4个进程变成了只有1个进程在运行,原因是内存超了。。

    另外还有一个让人头疼的问题。在用 Process()的时候,如果有一个进程灭了,那么你不会收到任何提示 —— 没错,就是悄无声息地没了。。

  • 相关阅读:
    “孤立”用户
    MongoDB 维护Replica Set
    Design7:数据删除设计
    abap取中值的函数
    REPLACE...IN.....WITH.... 的使用
    ABAP中RETURN与EXIT语句的区别
    在一个程序中调用另一个程序并且传输数据到选择屏幕执行这个程序
    Extract Datasets
    事件
    计算字符串长度的实例
  • 原文地址:https://www.cnblogs.com/manqing/p/5399358.html
Copyright © 2011-2022 走看看