首页 > 解决方案 > Python:在分析内存时增加递归函数调用

问题描述

我正在尝试研究递归函数。我正在使用 Colaboratory 运行我的代码。

这是我的问题:为什么在使用内存分析器时函数调用的数量会增加?我使用了一个全局变量来计算函数被调用的次数。

这是我没有探查器的原始代码:

# imports and other things...

accm = 0
def sum_num(n):
    global accm
    accm += 1
    if n == 1:
        return 1
    return n + sum_num(n - 1)

call_list = []
for i in range(0, 6000, 100):
    accm = 0
    if i == 0:
        i = 1
    sum_num(i)
    call_list.append(accm)

# visualization things...

并将递归限制设置为 10000。

可视化的图形accm从 1 到 6000 是线性的:

我预计当我使用分析器时这不会改变,但确实如此。

这是配置文件的版本:

# imports and other things...
from memory_profiler import memory_usage

accm = 0
def sum_num(n):
    global accm
    accm += 1
    if n == 1:
        return 1
    return n + sum_num(n - 1)


call_list = []
for i in range(0, 6000, 100):
    accm = 0
    if i == 0:
        i = 1
    _ = max(memory_usage((sum_num, (i,))))
    call_list.append(accm)

# visualization things...

而我得到的是一个从1到16000左右的非线性图;它的最大值约为 20000,它随着噪声线性增加,直到 x = 25 和 y = 16000,然后减小,然后随着噪声再次线性增加:

我想知道为什么会发生这种情况,以及如何解决它。

标签: pythonrecursiongoogle-colaboratorymemory-profiling

解决方案


您正在使用的探查器将多次运行您传递给的函数,memory_usage因为它会尝试识别内存采样间隔,以便获得足够的样本以获得准确的读数。如果您的代码运行得太快,则需要进一步缩小样本量,因此该函数将被更频繁地调用。

您可以memory_profiler. 这是重要的循环(去掉了不相关的东西):

while True:
    child_conn, parent_conn = Pipe() # this will store MemTimer's results
    p = MemTimer(os.getpid(), interval, child_conn, backend,
                 timestamps=timestamps,
                 max_usage=max_usage,
                 include_children=include_children)
    #...
    returned = f(*args, **kw)
    #...
    n_measurements = parent_conn.recv()
    #...
    if n_measurements > 4 or interval < 1e-6:
        break
    interval /= 10.

我对你的输出图的解释是,当你的函数被一个小的输入调用时,interval需要缩小三倍才能使分析器有足够的分辨率来准确测量内存使用情况,所以调用递归函数的次数是四倍正常水平。迭代22次左右后,区间只需要缩小两次,所以图形值变为正常水平的三倍。尽管代码运行异常快或慢会导致需要进行异常数量的间隔更改,但也存在一些噪音。这就是为什么您会看到一些需要较少调整大小的低谷,以及一个发生额外调整的高峰。

如果您将适当的interval参数传递给memory_usage. 默认值为0.1,因此您可以通过0.0001作为初始值传递来避免所有调整大小(但考虑进行试验,您可能可以做0.0005或更多):memory_usage((sum_num, (i,)), 1e-4)


推荐阅读