当前位置: 首页 > 知识库问答 >
问题:

Python多重处理-为什么使用functools.partial比默认参数慢?

卫松
2023-03-14

请考虑以下功能:

def f(x, dummy=list(range(10000000))):
    return x

如果我使用multiprocessing.pool.imap,我将获得以下定时:

import time
import os
from multiprocessing import Pool

def f(x, dummy=list(range(10000000))):
    return x

start = time.time()
pool = Pool(2)
for x in pool.imap(f, range(10)):
    print("parent process, x=%s, elapsed=%s" % (x, int(time.time() - start)))

parent process, x=0, elapsed=0
parent process, x=1, elapsed=0
parent process, x=2, elapsed=0
parent process, x=3, elapsed=0
parent process, x=4, elapsed=0
parent process, x=5, elapsed=0
parent process, x=6, elapsed=0
parent process, x=7, elapsed=0
parent process, x=8, elapsed=0
parent process, x=9, elapsed=0

现在,如果我使用funcools.partial而不是使用默认值:

import time
import os
from multiprocessing import Pool
from functools import partial

def f(x, dummy):
    return x

start = time.time()
g = partial(f, dummy=list(range(10000000)))
pool = Pool(2)
for x in pool.imap(g, range(10)):
    print("parent process, x=%s, elapsed=%s" % (x, int(time.time() - start)))

parent process, x=0, elapsed=1
parent process, x=1, elapsed=2
parent process, x=2, elapsed=5
parent process, x=3, elapsed=7
parent process, x=4, elapsed=8
parent process, x=5, elapsed=9
parent process, x=6, elapsed=10
parent process, x=7, elapsed=10
parent process, x=8, elapsed=11
parent process, x=9, elapsed=11

为什么使用functools.partial的版本要慢得多?

共有1个答案

笪烨
2023-03-14

使用multiprocessing需要向工作进程发送有关要运行的函数的信息,而不仅仅是要传递的参数。通过在主进程中对该信息进行酸洗,将其发送到辅助进程,然后在那里对其进行解酸洗来传输该信息。

这就引出了首要问题:

使用缺省参数处理函数是很便宜的;它只知道函数的名称(加上让Python知道它是一个函数的信息);辅助进程只查找该名称的本地副本。他们已经有一个命名函数f要查找,因此传递它几乎不需要花费任何费用。

但是,对部分函数进行处理涉及到底层函数(廉价)和所有默认参数(当默认参数是10M长的列表时,代价很大)。因此,每次在partial情况下调度任务时,都会对绑定参数进行酸洗,并将其发送到辅助进程,辅助进程将解酸洗,然后最后执行“真正”的工作。在我的机器上,pickle的大小大约为50 MB,这是一个巨大的开销;在我的机器上的快速定时测试中,对一个1000万长的列表0进行酸洗和解酸洗大约需要620 ms(这忽略了实际传输50 MB数据的开销)。

partials不得不这样腌制,因为他们不知道自己的名字;当处理像f这样的函数时,f(即def-ed)知道它的限定名(在交互式解释器中或从程序的主模块中,它是__main__.f),因此远程端可以通过执行与from_main__importf等价的操作在本地重新创建它。但partial不知道它的名称;当然,您将它分配给了g,但是picklepartial本身都不知道它可以使用限定名__main__.g;它可以被命名为foo.fred或其他很多东西。因此它必须pickle从零开始重新创建它所必需的信息。它还对每个调用(而不是每个工作者一次)进行pickle-ing,因为它不知道可调用对象在父级工作项之间没有变化,并且它总是试图确保它发送最新的状态。

您还有其他问题(只在partial情况下创建list和调用partial包装函数相对于直接调用该函数的少量开销),但这些都是相对于每次调用开销的小变化(初始创建list所增加的一次性开销是每个pickle/unpickle周期开销的一半以下;通过partial调用的开销不到一微秒)。

 类似资料:
  • 问题内容: 考虑以下功能: 如果使用,则会得到以下计时: 现在,如果我使用而不是使用默认值: 为什么版本使用速度这么慢? 问题答案: 使用要求向工作进程发送有关要运行的功能的信息,而不仅仅是发送要传递的参数。通过在主流程中酸洗该信息,将其发送到工作流程,然后在此处将其取消酸洗,来传输该信息。 这导致了主要问题: 用默认参数腌制一个函数很便宜 ; 它只会腌制函数的名称(加上让Python知道它是一个

  • 问题内容: 为什么这段代码会引发SyntaxError? 尽管以下代码段运行时没有可见错误: 问题答案: 必须将所有必需的参数放在任何默认参数之前。仅仅是因为它们是强制性的,而默认参数不是必需的。从语法上讲,如果允许使用混合模式,解释器将 无法 决定哪些值与哪些参数匹配。如果参数的输入顺序不正确,则会引发A : 让我们使用您的函数来查看关键字参数。 假设其允许声明函数如上,然后使用上述声明,我们可

  • 问题内容: 我正在尝试简化我的作业问题之一,并使代码更好一点。我正在使用的是二进制搜索树。现在,我的班级中有一个函数可以查找所有元素并将它们放入列表中。 然后我使用makeList()函数从树中取出所有节点,并将它们放入列表中。要调用该函数,我要做。对我来说,这似乎有些重复。我已经用调用了树对象,所以只是浪费了一点输入。 现在,makeList函数为: 我想使aNode输入一个默认参数,例如(它不

  • 我们知道,在调用函数时如果不指定某个参数, Python 解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。 Python 定义带有默认值参数的函数,其语法格式如下: def 函数名(...,形参名,形参名=默认值):     代码块

  • 在Spring MVC中,类DefaultAnnotationHandlerMapping已被弃用。文档(http://docs . spring . io/spring/docs/current/javadoc-API/org/spring framework/web/servlet/MVC/annotation/defaultannotationhandlermapping . html)说:

  • 问题内容: 部分应用程序很酷。无法提供哪些功能? 是更有效还是更易读? 问题答案: 无法提供哪些功能? 在额外功能方面并没有太多(但是,请参阅稍后)–可读性在旁观者的眼中。 大多数熟悉函数式编程语言的人(尤其是系列的人)看起来都lambda很好–我说“大多数”,绝对不是全部,因为和我肯定是“熟悉”的人(等) 却被认为是lambdaPython中的一个令人眼花eyes乱的异常…… 他为曾经接受过Py