需求:一个步骤非常多,执行时间很长的脚本。在其中某些时刻,可以创建一个检查点文件,把当前状态(变量、执行上下文之类的)给保存下来。如果当前进程在某个时刻因为某些原因被 kill 了,可以从之前保存的检查点处继续(除了系统时间、进程号之类的变了,其他的都一样)。就像游戏里面的手动存档机制一样。
类似于:
a = do_something()
checkpoint.save("ck01.bin")
b = do_other_thing1(a)
do_other_thing2(a, b)
例如正在执行do_other_thing2(a, b)
的时候被 kill 了,可以恢复从ck01.bin
文件里面恢复
checkpoint.load("ck01.bin")
checkpoint.continue()
就会从b = do_other_thing1(a)
处开始重新执行。
不知道 V 友们有没有见过比较成熟的实现类似功能的包,或者需要参考哪些方面的内容。 (很久之前我自己是糊了一个,但是用起来限制太大。所有要保存的参数必须放到一个字典里面,并且各种手动传递,非常蛋疼)
1
est 2022-05-09 15:47:05 +08:00
请使用泡菜(pickle)模块
|
2
xupefei 2022-05-09 15:47:06 +08:00 via iPhone
dill
|
3
Latin 2022-05-09 15:49:59 +08:00
|
5
bbbbbbbbbyron 2022-05-09 15:56:19 +08:00
可以试试 joblib, 提供自动的 checkpoint 管理
|
7
CatCode OP joblib 似乎不是专为这个目的开发的,看了一下官网的例子 用起来似乎不那么直观,更多的是用于缓存某些变量的计算进度。
python-checkpointing2 似乎对 CPython 的依赖度非常高,如果不采用 CPython 可能会炸 dill 似乎也非常依赖于 CPython ?看了 dill 的 test_session.py 依然云里雾里,不知道怎么用 |
9
LeeReamond 2022-05-09 17:21:12 +08:00
@CatCode 感觉 cpython 似乎并无此类功能,要不然 jupyter notebook 也无需用到 ironpython 。自己实现的话多进程显然是一个简单的方案,特定点位 fork 一下就什么都有了,如果一定要落盘的话,就算追踪字节码能够实现某种程度的状态保存,遇到不可序列化的或者复杂对象也不太好办
|
10
lolizeppelin 2022-05-09 18:26:23 +08:00
taskflow 支持保存
但是只能保存基本数据结构,自定义类可以和字典互相转化就可以保存 或者说 taskflow 就是干这个的 |
11
lolizeppelin 2022-05-09 18:26:47 +08:00
|
12
lolizeppelin 2022-05-09 18:30:30 +08:00
哦你要保存本身进程信息啊...额这...
推荐错了 |
13
CatCode OP @lolizeppelin 233333
不过这个我大概看了一下文档,也获得了一些“如果要自己做,哪些功能可以搞一搞”的启发 |
14
lolizeppelin 2022-05-09 19:27:29 +08:00
-_,- 如果可以参考直接用也是不错的
这玩意本来就是用来处理复杂冗长的虚拟机创建流程而开发的 |
15
ch2 2022-05-09 19:38:03 +08:00
你想要这么 toplevel 的 api ,不太可能
你用的端口、pid 、文件被别的进程占用或者改动了咋办?你建立的 tcp 长连接被 reset 了咋办?直接 crash? 对整个进程 checkpoint 的工具确实有,它叫 CRIU 不过如果你不配合容器一起用,这玩意基本上没有可用性的 而且它对进程 checkpoint 之后的外部环境不可变性要求非常高,提到的那几个条件必须全部满足才能保证 restore 后的进程仍然能跑 |
16
sujin190 2022-05-09 19:42:26 +08:00
pyflink 这种能满足?
|
17
CatCode OP @ch2 我既然有这个需求,端口、文件、TCP 连接这些肯定会添加额外的代码处理,或者这些方面,我添加检查点的位置会在没有 TCP 链接、所有打开的文件已经关闭这种情况下进行。类比于游戏,游戏里可不是随时都能手动存档的。
换句话说,我要处理的任务可能是分段的:前一段和后一段不搭着,只有一些变量要传递。 |
18
ch2 2022-05-10 00:01:35 +08:00
@CatCode #17 在你能够保证严格的外部条件不变的情况下,可以手动调用 docker checkpoint create 命令将 python 进程制作为快照,再使用 docker checkpoint restore 命令进行还原:
https://docs.docker.com/engine/reference/commandline/checkpoint/ |
19
CatCode OP @ch2 这个是依赖于 docker 的 依赖外部环境而不是 Python 本身了
如果可以考虑外部软件,不考虑“重量”的话,直接用虚拟机快照效果更好,端口文件那些都不需要考虑了(笑) 我期望的是 Python 自身实现 |
20
ch2 2022-05-10 10:21:00 +08:00 via iPhone
@CatCode 你可以参考 CRIU 的代码,为了保存和恢复一个进程的运行时状态他们做了多少 hack ,我相信没有人比他们更有毅力跟恒心坚持于这个目标。你想要的无非是一个 Python 版本的 CRIU 实现,或者是一个 Python 的 libcriu 引用。
但是不使用容器,非常难以避免因为环境改变导致的进程还原冲突。举个例子:为了避免 pid 被别的进程占了无法还原,你得在一开始就用 pid namespace 给你的 Python 进程开一个命名空间,但是容器在一开始创建的隔离环境就包含了各种命名空间,顺手把网络跟存储挂载都解决了。而且你的进程必须脱离 tty 运行,否则还原的时候找不到之前的 shell 也还原不成直接 core dump 。 这也就是只有容器才提供了进程级别 checkpoint 功能的原因,你自己想做进程的 checkpoint 最后无非都还要走一遍容器的老路,否则你会遇到各种 bad case ,无法 checkpoint 无法 restore 。 最简单的例子你可以 apt-get install criu ,然后试试 criu dump 你的 Python 进程,看看能不能顺利 dump |
21
CatCode OP @ch2 我说过我并非想处理每一种情况。我已经注明了不考虑多线程 /多进程——意味着我创建检查点的时候只有一个线程和进程。17 楼里我也说了检查点的创建是在没有 TCP 链接、所有打开的文件已经关闭的情况。我的任务也不依赖于 PID 号,所以变了也无所谓。17 楼真的说的很明白了,是一段一段地处理大量操作。如果还不明白,找个类比,GitHub Actions 里面的 steps 。
|
22
gengchun 2022-05-10 11:25:34 +08:00
前段时间有个类似的需求,我是每步直接缓存结果,类似于 http GET 缓存的处理方式,通过一个标志符和一个上下文 hashmap 做键值。每步就检查有没有缓存,有就直接返回缓存结果,没有就算一次。
检查缓存的逻辑本身,当然可以用装饰器语法加上。不需要考虑多子任务,并发结果合并什么的,感觉用不到什么高级的库。 只是想开拓一下思路。 |