【问题标题】:How to checkpoint a long-running function pythonically?如何以 python 方式检查长时间运行的函数?
【发布时间】:2015-12-08 12:24:02
【问题描述】:

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

实现检查点的pythonic方法是什么?

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

另外,我正在考虑将此类函数转换为一个类(见下文)。函数的参数将成为构造函数的参数。构成算法状态的中间数据将成为类属性。 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))

【问题讨论】:

  • 我认为你将长期运行的函数变成一个类的想法是个好主意。
  • 如果你的长期运行函数的状态是明确定义的,那么你可以显式地存储它(如果需要,创建一个类并实现泡菜支持)。在更复杂的情况下可能会失败;除非你从头开始设计你的程序来支持它(Lisp,Smalltalk)¶ 要暂时断电,你可以hibernate your computer¶ 一般来说,你可以在 VM 中运行计算,然后 move it if necessary。在 docker 中有一个实验性的 checkpoint & restore 支持。

标签: python pickle checkpointing


【解决方案1】:

通常答案是使用您最喜欢的序列化方法进行序列化,即 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

【讨论】:

    【解决方案2】:

    如果您愿意在编写代码时将检查点作为头等大事,那么您建议的类结构是一个不错的选择。如果您想在事后才引入检查点,尽管您可以使用专用的检查点 Linux 包 like this one 或 Python 特定的包,如 this one(我写的)。

    【讨论】:

      【解决方案3】:

      这是评论,不是答案。

      一个用于随时检查和恢复 Python 函数、类或程序的通用机制将很难构建。 简单的部分是检查点和恢复属性都是基本数据类型或基本数据类型集合的类实例的机制。 然后get_state 方法可以打包类的运行实例的状态,restore_state(checkpointed_state) 方法可以恢复状态。

      但程序的许多部分并不是基本数据类型的集合。其中包括:

      • 程序指针
      • 对方法和函数的引用
      • 打开文件句柄、网络套接字等
      • 运行子进程

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-23
        相关资源
        最近更新 更多