Python 3多进程池

3
我正在学习使用多进程池技术。我写了这个脚本来练手。
有人可以告诉我为什么使用普通的for循环比使用进程池要快吗?
附注:我的CPU有2个核心。
非常感谢。
from multiprocessing import Pool
from functools import reduce
import time

def one(n):
    a = n*n
    return a 

if __name__ == '__main__':
    l = list(range(1000))

    p = Pool()
    t = time.time()
    pol = p.map(one, l)
    result = reduce(lambda x,y: x+y, pol)
    print("Using Pool the result is: ", result, "Time: ", time.time() - t )
    p.close()
    p.join()

    def two(n):
        t = time.time()
        p_result = [] 

        for i in n:
            a = i*i 
            p_result.append(a)

        result = reduce(lambda x,y: x+y, p_result)
        print("Not using Pool the result is: ", result, "Time: ", time.time() - t)

    two(l)

使用进程池,结果为:332833500时间:0.14810872077941895

未使用进程池,结果为:332833500时间:0.0005018711090087891

3个回答

4
我认为这里有几个原因,但我猜想主要与运行多个进程的开销有关,这主要与同步和通信有关,还有您的非并行化代码编写得更加高效。
作为基础,以下是您未经修改的代码在我的计算机上运行的方式:
('Using Pool the result is: ', 332833500, 'Time: ', 0.0009129047393798828)
('Not using Pool the result is: ', 332833500, 'Time: ', 0.000598907470703125)

首先,我希望通过使two()函数的代码与并行化代码几乎相同来尝试平衡竞技场。这是修改后的two()函数:

def two(l):
    t = time.time()

    p_result = map(one, l)

    result = reduce(lambda x,y: x+y, p_result)
    print("Not using Pool the result is: ", result, "Time: ", time.time() - t)

现在,在这种情况下,实际上并没有什么太大的区别,但是在接下来的一秒钟内,看到两种情况都在做完全相同的事情将变得很重要。以下是使用此更改的示例输出:
('Using Pool the result is: ', 332833500, 'Time: ', 0.0009338855743408203)
('Not using Pool the result is: ', 332833500, 'Time: ', 0.0006031990051269531)

现在我想要说明的是,由于one()函数计算成本非常低,进程间通讯的开销超过了并行运行它的好处。我将按照以下方式修改one()函数,强制它进行一些额外的计算。请注意,由于对two()函数的更改,这个变化将影响并行和单线程代码。
def one(n):
    for i in range(100000):
        a = n*n
    return a

for循环的原因是为了给每个进程一个存在的理由。在您的原始代码中,每个进程只是执行几次乘法,然后必须将结果列表发送回父进程,并等待接收新的数据块。发送和等待所需的时间比完成单个数据块要长得多。通过添加这些额外的周期,它可以强制每个数据块需要更长的时间来完成,而不会改变进程间通信所需的时间,因此我们开始看到并行性的效果。当我运行带有对one()函数进行更改的代码时,以下是我的结果:

('Using Pool the result is: ', 332833500, 'Time: ', 1.861448049545288)
('Not using Pool the result is: ', 332833500, 'Time: ', 3.444211959838867)

所以,你需要做的就是让你的子进程多做一点工作,这样它们就更值得你的付出了。

非常感谢TallChuck,您的回答非常启发人。 - max fraguas

-2

使用Pool时,Python使用全局解释器锁来同步多个进程中的多个线程。也就是说,当一个线程在运行时,所有其他线程都会停止/等待。因此,您将体验到顺序执行而不是并行执行。在您的示例中,即使您在pool中分布在多个线程之间,由于全局解释器锁,它们也会按顺序运行。这也增加了调度的很多开销。

从Python文档中关于全局解释器锁的描述:

CPython解释器用于确保仅有一个线程同时执行Python字节码的机制。这简化了CPython实现,使对象模型(包括关键内置类型,如dict)隐式安全地防止并发访问。锁定整个解释器使解释器更容易成为多线程的,但代价是牺牲了多处理器机器所提供的大部分并行性。

因此,你所实现的并不是真正的并行处理。如果你需要在Python中实现真正的多进程能力,你需要使用Processes,这将导致你使用Queues来在进程之间交换数据。

全局解释器锁只在单个进程内有效。如果您在运行“池”时运行“ps”,则会发现它实际上正在使用多个进程。 - TallChuck
是的,你的观点是正确的。但根据我的个人经验,在使用线程而不是进程时,全局解释器锁会产生影响。你怎么看? - Imesha Sudasingha
当然,但是所呈现的代码并没有使用线程,除了在multiprocessing库中处理进程时使用了threading库。 - TallChuck
我明白了,感谢您提供的详细信息。 - Imesha Sudasingha

-3
多进程包提供本地和远程并发,通过使用子进程而不是线程有效地绕过全局解释器锁。因此,多进程模块允许程序员充分利用给定机器上的多个处理器。
在Python 2.7.16文档的基于进程的“线程”接口章节中找到。

看起来你是从 https://docs.python.org/2/library/multiprocessing.html 复制的,如果是这样,请注明出处,并且要清楚地表明这不是你自己的文字。另外,最好不要写一些仅仅是外部资源文本复制的答案。将它们作为备选方案,而不是你答案的核心。 - rene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接