调试慢程序;从中间重启

15 投票
6 回答
1576 浏览
提问于 2025-04-17 00:42

我有一个程序,运行起来很慢,我想调试一下算法。每次都要从头开始运行程序实在太麻烦了,我希望能从程序的中间部分重新开始。你能想出什么聪明的方法来实现这个吗?

我最初的想法是设置一些检查点(也就是我调用函数的地方),在这些检查点保存局部变量和大数据,可以用pickle或者sqlite(用sqlite可以查看中间的数据)。之后我可以尝试让程序从某个特定的检查点重新开始。不过,我不想为了这个目的把所有的代码块都分开。

有没有人有聪明的主意来解决这个调试的问题?

6 个回答

4

Joblib 可以很方便地帮你保存计算结果。下面是他们文档中的一个例子:

>>> from joblib import Memory
>>> mem = Memory(cachedir='/tmp/joblib')
>>> import numpy as np
>>> a = np.vander(np.arange(3))
>>> square = mem.cache(np.square)
>>> b = square(a)
________________________________________________________________________________
[Memory] Calling square...
square(array([[0, 0, 1],
       [1, 1, 1],
       [4, 2, 1]]))
___________________________________________________________square - 0.0s, 0.0min

>>> c = square(a)
>>> # The above call did not trigger an evaluation because the result is cached

计算的结果会自动保存在你的电脑上,所以 Joblib 可能会很适合你的需求。

7

让你的程序更模块化。理想情况下,主要的代码块应该看起来像这样:

import config
import my_numerics
import post_processing

my_numerics.configure(config.numerics)
values = my_numerics.run()

post_processing.run(values, config.post_processing)

你明白这个意思了吧。这样就很容易创建一个“模拟”的数字对象,它可以返回预先计算好的数据,然后把这个数据传入后续处理。


补充说明:我还是不太明白。下面的伪代码是否准确地描述了你的问题?

for _ in range(lots):
    do_slow_thing_one()

for _ in range(many):
    do_slow_thing_two()

for _ in range(lots_many)
    do_slow_thing_three()

也就是说,你想在数字计算进行到一半的时候打断它(而不是在最后),比如在第三个循环开始的时候,而不需要重新运行前两个循环?

如果是这样的话,即使循环里面的代码不多,你也应该把设计模块化:

input_data = np.load(some_stuff)
stage_one = do_thing_one(input_data)
stage_two = do_thing_two(stage_one)
stage_three = do_thing_three(stage_two)

第一种方法是通过一个隐式接口在不同阶段之间传递数据;也就是使用局部变量的字典。这种方法不好,因为你没有定义哪些变量在被使用,因此你无法为测试或调试模拟这些变量。第二种方法是在你的函数之间定义一个(基础的)接口。现在你不再关心 do_thing_one 做了什么,只要它接受一些输入数据并返回一些输出数据就行。这意味着要调试 do_thing_three 时,你只需要这样做:

stage_two = np.load(intermediate_stuff)
stage_three = do_thing_three(stage_two)

只要 stage_two 中的数据格式正确,数据来自哪里就无所谓了。

1

我在谷歌上搜索时发现了一个叫 CryoPID 的工具,如果你是在基于Linux的系统上开发的话,这个工具可能会帮到你。它声称可以暂停一个正在运行的程序,并把它保存到一个文件里,然后再在不同的电脑上重新启动这个程序。不过我自己还没有试过这个工具。

撰写回答