如何腌制“记忆化”的 Python 函数?

Posted

技术标签:

【中文标题】如何腌制“记忆化”的 Python 函数?【英文标题】:How do I pickle a "memoized" Python function? 【发布时间】:2021-02-14 09:51:16 【问题描述】:

我有以下代码:

def f(input,MEM=):
    if len(MEM) == 0:
        with open('dill.pkl', 'rb') as f:
            MEM = dill.load(f)
    if input not in MEM:
        intended_output = complex_function(input)
        MEM[input] = intended_output
    return MEM[input]

运行大量输入时,我发现我的代码运行速度比我最初没有加载 MEM 时要慢得多。即,与

def f(input,MEM=):
    if len(MEM) == -1:
        return None
    if input not in MEM:
        intended_output = complex_function(input)
        MEM[input] = intended_output
    return MEM[input]

我同时运行ff2 来处理四千个输入,f 需要半小时才能完成,但 f2 只需 40 秒即可完成。这是因为当我用莳萝加载MEM 时,它由访问速度较慢的结构表示?我尝试过复制和深度复制 MEM,这只会使问题变得更糟(尤其是深度复制,即使是较小的输入也需要几秒钟的时间)。

【问题讨论】:

不确定是否与问题有关,但(1)如果使用with open(...),则不需要f.close(); (2) 你根本没有使用f!? 糟糕,我是想写MEM = dump(f)。我不知道f.close() 是不必要的,我现在也更改了。 不幸的是,这并没有明显改变我的代码速度。 如果您想添加自己的答案,请将其添加为答案,而不是将其编辑到问题中。 我是dill 作者。您可能想查看klepto 如何使用dillklepto 是一个缓存库,它利用 dill 来帮助进行记忆.. 【参考方案1】:
MEM = dill.load(...)

这会创建一个名为 MEM 的新局部变量,但不会更改默认参数 MEM

因此,默认参数MEM 是一个空字典,并且每次调用该函数时都会取消文件。

要实际更改默认参数,您可以简单地使用:

MEM.update(dill.load(...))

见https://docs.python.org/3/library/stdtypes.html#dict.update。

【讨论】:

【参考方案2】:

    最好只取消腌制一次或尽可能少。

    在许多情况下运行该函数时,最好避免循环并改用诸如利用 C 代码的 .apply 之类的函数。

    也许您可以并行化,因此您可以使用所有内核来完成任务。也许这有帮助:https://***.com/a/66079049/7127519

【讨论】:

我知道 for 循环不是主要问题。我在f2 中包含了虚拟循环,它运行得非常快。关于(1),我如何只取消腌制一次?我对莳萝很陌生。 也许您可以在函数外部和开头取消选择。 我的意思是,第一个 if 语句只会为真一次。因此,我认为这没有用...哦,除非这可能导致 MEM 在每次调用后都无法保存!

以上是关于如何腌制“记忆化”的 Python 函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Clojure 中生成记忆递归函数?

函数式编程-记忆化缓存

为啥记忆化不是语言功能?

函数 记忆化搜索模型

Koltin 递归尾递归和记忆化

Python - cache修饰器:将已经计算过的结果保留下来,可用于记忆化搜索