当前位置: 首页 > 科技观察

认为Python太慢?ParallelcomputingProcessPools三行代码给你4倍的速度!

时间:2023-03-18 18:21:09 科技观察

作者|由AdamGeitgey编译|YuanYuan、Lisa、Saint、AileenPython绝对是处理数据或自动执行重复性任务的最佳编程语言。想要抓取网络日志?或者调整一百万张图片的大小?总有对应的Python库让你轻松完成任务。然而,Python的运行速度一直饱受诟病。默认情况下,Python程序在单个CPU上使用单个进程。如果你的电脑是最近十年生产的,在大多数情况下会有4个或更多的CPU核心。也就是说,当你在等待程序运行完毕的时候,你的电脑有75%甚至更多的计算资源是空的!我们来看看如何通过并行计算来充分利用计算资源。得益于Python的concurrent.futures模块,只需要3行代码就可以让一个普通的程序并行运行。一般情况下的Python操作比如我们有一个放满图片文件的文件夹,我们想为每张图片创建一个缩略图。在下面的小程序中,我们使用Python自带的glob函数获取一个文件夹中所有图片文件的列表,并使用Pillow图像处理库获取每张图片的128像素缩略图。该程序遵循一种非常常见的数据处理模式:从一系列要处理的文件(或其他数据)开始编写辅助函数来处理数据使用for循环调用辅助函数来逐个处理数据让我们用1000张图片来测试程序,看看运行时间是多少。程序运行8.9秒,但计算机的计算资源是多少?让我们再次运行程序并检查活动监视器:计算机有75%是空的,这是为什么?问题是我的电脑有4个CPU内核,但Python只使用其中一个内核。即使我的程序完全占据了那个CPU核心,其他3个CPU核心什么也没做。我们需要想办法把整个程序的工作负载分成4个部分并行运行。幸运的是,Python可以做到这一点!让我们尝试并行计算下面是实现并行计算的一种方法:将Jpeg图像文件列表分成4部分。同时运行四个Python解释器。让四个解释器分别处理部分图像文件。将四个解释器的结果汇总得到最终结果。4个Python程序在4个CPU上运行,比在1个CPU上运行快4倍左右,对吧?好消息是Python可以帮助我们解决并行计算中比较麻烦的部分。我们只需要告诉Python我们要运行什么功能,我们希望把工作分成多少部分,剩下的交给Python。我们只需要修改三行代码。首先,我们需要导入concurrent.futures库。这个库是Python自带的:然后,我们需要告诉Python启动4个额外的Python实例。我们通过创建进程池来传达指令:默认情况下,上面的代码会为计算机的每个CPU创建一个Python进程,所以如果你的计算机有4个CPU,则会启动4个Python进程。最后一步是让进程池使用这4个进程来执行我们对数据列表的辅助函数。我们可以将之前的for循环替换为:新代码是调用executor.map()函数。调用executor.map()函数时,需要输入辅助函数和需要处理的数据列表。这个函数为我们做了所有艰苦的工作,将列表拆分成更小的列表,将更小的列表分配给每个子进程,运行子进程,并聚合结果。不错的工作!我们还可以获得每次调用辅助函数的结果。executor.map()函数按输入数据的顺序返回结果。Python的zip()函数可以一步得到原始文件名和对应的结果。下面是三处改动后的程序:让我们试着运行一下,看看它是否缩短了运行时间:2.274秒,程序运行起来了!这是原始版本的4倍加速。运行时间缩短的原因恰恰是我们这次将1个CPU换成了4个CPU。但如果仔细观察,您会发现“User”时间约为9秒。如果程序在2秒内运行完毕,为什么客户端时间是9秒?好像……怎么了?实际上这是因为“用户”时间是所有CPU时间的总和。我们上次使用了9秒的总CPU。注意:启动Python进程和将数据分配给子进程都需要时间,因此使用此方法不一定会获得巨大的加速。如果您正在处理大量数据,这里有一篇文章“Tipsforsettingthechunksizeparameter”可能对您有所帮助:https://docs.python.org/3/library/concurrent.futures.html#concurrent。期货.执行者.map。这种方法总是会加快我的程序吗?当您有一列数据并且每个数据都可以独立处理时,使用进程池是一种很好的方法。以下是适合并行处理的一些示例:从一系列独立的Web服务器日志中抓取数据。从一堆XML、CSV和JSON文件中解析数据。预处理大量图像数据并构建机器学习数据集。但是进程池并不是唯一的。使用进程池需要在不同的Python进程之间来回传递数据。如果您正在处理的数据在处理过程中无法有效传递,则此方法将行不通。您处理的数据必须是Python知道如何处理的类型(https://docs.python.org/3/library/pickle.html#what-c??an-be-pickled-and-unpickled)。此外,数据未按预期顺序处理。如果您需要上一步的结果才能继续下一步,这也不起作用。吉尔呢?你可能听说过Python有一个全局解释器锁(GlobalInterpreterLock,简称GIL)。这意味着即使你的程序是多层的,每一层也只能执行一个Python命令。GIL确保在任何时候只有一个Python线程执行。GIL***的问题在于Python的多线程程序无法发挥多核CPU的优势。但是ProcessPools可以解决这个问题!因为我们运行的是单独的Python实例,所以每个实例都有自己的GIL。然后你就有了真正并行处理的Python代码!不要害怕并行处理!借助concurrent.futures库,Python允许您简单地修改脚本,但它可以立即调用您计算机上的所有CPU内核以满负荷运行。不要害怕尝试。一旦掌握了它,就像编写for循环一样简单,但会使整个程序更快。原文:https://medium.com/@ageitgey/quick-tip-speed-up-your-python-data-processing-scripts-with-process-pools-cf275350163a【本文为专栏组织原文翻译大数据文摘微信公众号《大数据文摘(id:BigDataDigest)》】点此查看作者更多好文