代码之家  ›  专栏  ›  技术社区  ›  NVaughan

Python多处理:有池和无池

  •  2
  • NVaughan  · 技术社区  · 9 年前

    我试图理解Python的多处理,并设计了以下代码来测试它:

    import multiprocessing
    
    def F(n):
        if n == 0: return 0
        elif n == 1: return 1
        else: return F(n-1)+F(n-2)
    
    def G(n):
        print(f'Fibbonacci of {n}: {F(n)}')
    
    processes = []
    for i in range(25, 35):
        processes.append(multiprocessing.Process(target=G, args=(i, )))
    
    for pro in processes:
        pro.start()
    

    当我运行它时,我告诉我计算时间大约为6.65秒。

    然后,我编写了以下代码,我认为这些代码在功能上等同于后者:

    from multiprocessing.dummy import Pool as ThreadPool
    
    def F(n):
        if n == 0: return 0
        elif n == 1: return 1
        else: return F(n-1)+F(n-2)
    
    def G(n):
        print(f'Fibbonacci of {n}: {F(n)}')
    
    in_data = [i for i in range(25, 35)]
    
    pool = ThreadPool(10)
    
    results = pool.map(G, in_data)
    
    pool.close()
    pool.join()
    

    运行时间约为12s。

    为什么第二个几乎是第一个的两倍?他们不应该是等价的吗?

    (注意,我正在运行Python 3.6,但也在3.52上测试了类似的代码,结果相同。)

    1 回复  |  直到 9 年前
        1
  •  1
  •   Gil Hamilton    9 年前

    第二种方法花费的时间是第一种方法的两倍,原因可能是CPython全局解释器锁定。

    从…起 http://python-notes.curiousefficiency.org/en/latest/python3/multicore_python.html :

    […]GIL有效地将字节码的执行限制在单个核上,从而使纯Python线程成为跨多个核分发CPU绑定工作的无效工具。

    如你所知, multiprocessing.dummy 是包裹在 threading 模块,所以您创建的是线程,而不是进程。全局解释器锁与这里的CPU绑定任务没有太大区别,只是在单个线程中顺序执行Fibonacci计算(除了添加了一些线程管理/上下文切换开销)。

    在“真正的多处理”版本中,每个进程中只有一个线程,每个线程都使用自己的GIL。因此,您实际上可以使用多个处理器来提高速度。

    对于这个特定的处理任务,有 在多个进程上使用多线程的显著优势。如果您只有一个处理器,那么使用 任何一个 多进程 单个线程/进程上的多个线程(实际上,这两个线程都只会增加任务的上下文切换开销)。

    (FWIW:A join 事实上 multiprocessing 版本显然是由python运行时自动完成的,因此添加显式 参加 在我的测试中,使用 time(1) 顺便说一下,如果你 想添加 参加 ,您应该添加 第二 循环 参加 处理。添加 参加 现有的循环将简单地序列化您的进程。)