如何通过pythonically检查长时间运行的函数?

时间:2015-12-08 12:24:03

标签: python pickle checkpointing

计算科学的典型情况是让程序连续运行几天/几周/几个月。由于硬件/ OS故障是不可避免的,因此通常利用检查点,即不时地保存程序的状态。如果发生故障,将从最新检查点重新启动。

实施检查点的pythonic方式是什么?

For example,可以直接转储函数的变量。

或者,我正在考虑将这样的函数转换为类(见下文)。函数的参数将成为构造函数的参数。构成算法状态的中间数据将成为类属性。 pickle模块将有助于(反)序列化。

import pickle

# The file with checkpointing data
chkpt_fname = 'pickle.checkpoint'

class Factorial:
    def __init__(self, n):
        # Arguments of the algorithm
        self.n = n

        # Intermediate data (state of the algorithm)
        self.prod = 1
        self.begin = 0

    def get(self, need_restart):
        # Last time the function crashed. Need to restore the state.
        if need_restart:
            with open(chkpt_fname, 'rb') as f:
                self = pickle.load(f)

        for i in range(self.begin, self.n):
            # Some computations
            self.prod *= (i + 1)
            self.begin = i + 1

            # Some part of the computations is completed. Save the state.
            with open(chkpt_fname, 'wb') as f:
                pickle.dump(self, f)

            # Artificial failure of the hardware/OS/Ctrl-C/etc.
            if (not need_restart) and (i == 3):
                return

        return self.prod


if __name__ == '__main__':
    f = Factorial(6)
    print(f.get(need_restart=False))
    print(f.get(need_restart=True))

3 个答案:

答案 0 :(得分:1)

如果您愿意将检查点作为头等大事来编写代码,那么您建议的类结构就是一个不错的选择。如果想事后引入检查点,虽然可以使用特殊目的的检查点Linux软件包like this one或Python专用的软件包this one(我写过)。

答案 1 :(得分:1)

这是评论,而不是答案。

很难随时建立用于检查点和还原Python函数,类或程序的通用机制。 最简单的部分是一种检查点并还原其属性均为基本数据类型或基本数据类型集合的类实例的机制。 然后,get_state方法可以打包该类正在运行的实例的状态,而restore_state(checkpointed_state)方法可以恢复该状态。

但是程序的许多部分都不是基本数据类型的集合。这些包括:

  • 程序指针
  • 对方法和功能的引用
  • 打开文件句柄,网络套接字等
  • 正在运行的子流程

这些都很难检查和恢复,因为很难获得它们的全部状态,并且很难恢复它们的状态。

因此,鉴于您希望对长期运行的分析进行检查点/还原,我建议您使用更高级别的机制,例如Docker容器或Linux进程CRIU的检查点/还原。请注意,这些功能不会完全检查点并还原所有状态,但是它们比您可以构建的功能要多。

答案 2 :(得分:0)

通常答案是使用您最喜欢的序列化方法序列化为cpickle json或xml。 Pickle的优势在于,您可以在不需要额外工作的情况下反序列化整个对象。

此外,将进程与状态分开是个好主意,因此您只需序列化状态对象即可。很多对象不能被腌制,例如线程,但你可能想要运行许多工作者(虽然要小心GIL),所以酸洗会抛出你尝试腌制它们的例外。您可以通过_getstate__setstate_删除导致问题的条目来解决此问题 - 但如果您只是将流程和状态分开,则这不再是问题。

要检查点,请将检查点文件保存到已知位置,程序开始时,检查此文件是否存在,是否存在进程尚未启动,否则加载并运行它。创建一个线程,通过排空任何工作线程正在处理的队列,然后保存状态对象,定期检查运行任务,然后重用您使用的恢复逻辑,以便在检查点后恢复。

为了安全地检查您需要确保您的程序不会因中间泡菜死亡而损坏检查点文件。要做到这一点

  1. 在临时位置创建检查点并完成写作
  2. 将最后一个检查点重命名为checkpoint.old
  3. 将新检查点重命名为checkpoint.pickle
  4. 删除checkpoint.old
  5. 如果您启动该程序并且没有检查点但有checkpoint.old,则该程序在步骤2之后死亡,因此请加载checkpoint.old,将checkpoint.old重命名为checkpoint.pickle并正常运行。如果程序在其他任何地方死亡,您只需重新加载checkpoint.pickle