首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >multiprocessing.Pool对multiprocessing.pool.ThreadPool

multiprocessing.Pool对multiprocessing.pool.ThreadPool
EN

Stack Overflow用户
提问于 2022-01-13 17:37:22
回答 1查看 1.8K关注 0票数 0

下面是multiprocessing.Poolmultiprocessing.pool.ThreadPool和顺序版本的一些测试,我想知道为什么multiprocessing.pool.ThreadPool版本比顺序版本慢?

multiprocessing.Pool确实更快吗?因为它使用进程(即没有GIL)和multiprocessing.pool.ThreadPool使用线程(即GIL),尽管包的名称是multiprocessing

代码语言:javascript
复制
import time


def test_1(job_list):
    from multiprocessing import Pool

    print('-' * 60)
    print("Pool map")
    start = time.time()
    p = Pool(8)
    s = sum(p.map(sum, job_list))
    print('time:', time.time() - start)


def test_2(job_list):
    print('-' * 60)
    print("Sequential map")
    start = time.time()
    s = sum(map(sum, job_list))
    print('time:', time.time() - start)


def test_3(job_list):
    from multiprocessing.pool import ThreadPool

    print('-' * 60)
    print("ThreadPool map")
    start = time.time()
    p = ThreadPool(8)
    s = sum(p.map(sum, job_list))
    print('time:', time.time() - start)


if __name__ == '__main__':
    job_list = [range(10000000)]*128

    test_1(job_list)

    test_2(job_list)

    test_3(job_list)

输出:

代码语言:javascript
复制
------------------------------------------------------------
Pool map
time: 3.4112906455993652
------------------------------------------------------------
Sequential map
time: 23.626681804656982
------------------------------------------------------------
ThreadPool map
time: 76.83279991149902
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-01-13 17:57:03

您的任务完全是CPU约束的(没有I/O上的阻塞),并且没有使用任何扩展代码来手动释放GIL来执行大量的数字处理,而不需要Python级别的引用计数对象(例如,hashlib散列大型输入、大型数组numpy计算等)。因此,GIL的定义阻止您从代码中提取任何并行性;只有一个线程可以同时保存GIL并执行Python字节码,而您的执行速度较慢,因为:

  1. 您必须启动所有这些线程--
  2. --它们之间必须交出GIL来模拟并行处理
  3. --您必须清理所有的线程--

简而言之,是的,ThreadPool does what it says on the tin:它提供与Pool相同的API,但是由线程支持,而不是工作进程,因此不能避免GIL限制和开销。直到最近,它才被直接记录下来;相反,它是由multiprocessing.dummy文档间接记录的,这些文档更明确地说明了提供multiprocessing API,但是由线程支持,而不是进程(您使用它作为multiprocessing.dummy.Pool,实际上没有包含“线程”这个词)。

我将注意到,您的测试使Pool看起来比正常情况更好。通常,Pool在这样的任务中做得不好(大量数据,相对于数据大小计算很少),因为序列化数据并将其发送到子进程的成本超过了并行工作带来的微小收益。但是,由于您的“大数据”是由range对象表示的(这些对象以较低的成本序列化,作为对range类的引用和用于重构它的参数),所以很少有数据传输到和从工作人员之间传输。如果您使用真正的数据(实现了listint),Pool的好处就会急剧下降。例如,只需将job_list的定义更改为:

代码语言:javascript
复制
job_list = [[*range(10000000)]] * 128

我的机器上的Pool时间(未经修改的Pool情况需要3.11秒)跃升到8.11秒。甚至这也是一个谎言,因为pickle序列化代码识别重复重复的相同list,并且只序列化内部list一次,然后快速地“查看第一个list”代码来重复它。我告诉你什么用途:

代码语言:javascript
复制
job_list = [[*range(10000000)] for _ in range(128)]

对运行时进行了处理,但我几乎让我的机器崩溃,只是试图运行这一行(创建said list of lists需要大约46 GB的内存,在父进程中支付一次,然后在子进程中再支付一次);可以这么说,Pool将非常糟糕,特别是在数据只适合RAM一次、但不是两次的情况下。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70700809

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档