Python : 在分析内存时增加递归函数调用

Python : Increasing recursion function call while profiling memory

我正在尝试研究递归函数。我正在使用 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,减小,然后再次随噪声线性增加:

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

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

您可以在 source code of 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 需要 sh运行k 三次,然后分析器才有足够的分辨率来测量内存使用准确,因此递归函数的调用次数是正常水平的四倍。迭代 22 次左右后,间隔只需要 sh运行k 两次,因此图形值变为正常水平的三倍。尽管异常快或慢的代码 运行 导致需要完成异常数量的间隔更改,但也有一些噪音。这就是为什么您会看到一些需要较少调整大小的谷值,以及一个发生额外调整大小的峰值。

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