第二次调用时为什么函数调用次数不同?

3

我正在编写一个接口来帮助我对Python脚本进行分析。我借鉴了来自profile.py源代码的执行Python脚本的代码。我发现当我连续两次对相同的代码进行分析时,第二次返回的函数调用次数会有所不同。例如,在myscript.py上执行以下代码:

from cProfile import Profile
import sys
import os.path

for i in range(3):
    prof = Profile()

    progname = 'myscript.py'
    sys.path.insert(0, os.path.dirname(progname))
    with open(progname, 'rb') as fp:
        code = compile(fp.read(), progname, 'exec')
    globs = {
            '__file__': progname,
            '__name__': '__main__',
            '__package__': None,
            '__cached__': None,
            }
    prof.runctx(code, globs, None)
    prof.create_stats()
    print(len(prof.stats))

给我
511
30
30

作为输出结果,第二次调用函数的数量较少是为什么?哪个数字才是正确的数字?我该怎么做才能使两次获得相同的结果?


myscript.py 的内容如下:

import numpy
import numpy.linalg

if __name__ == '__main__':

    r = numpy.random.rand(1000, 1000)
    numpy.linalg.inv(r)

1
我无法复现它,但没有myscript.py中的代码很难说。 - eandersson
这可能有些偏题,但是你是否使用了任何缓存装饰器/记忆化或类似的东西?(另外,我只用过CPython,所以不确定其他实现是否会进行任何记忆化/缓存)。 - nvlass
我认为这与脚本中有import语句有关。 - Charles Brunet
第三次会给予什么? - tacaswell
第二次得到相同的结果。我已经用一个真实的例子更新了问题。 - Charles Brunet
1个回答

1
似乎函数调用计数不同的原因是由于在第二次运行代码时,myscript.py 导入的模块没有再次导入。为了获得一致的结果,首先可以在进行分析之前导入 myscript.py。但是,这意味着如果我导入的模块在导入时执行某些任务,则这些任务将无法被分析。
prof = Profile()

progname = 'myscript.py'
sys.path.insert(0, os.path.dirname(progname))
modname, _ = os.path.splitext(os.path.basename(progname))
__import__(modname, globals(), locals(), [], 0)
with open(progname, 'rb') as fp:
    code = compile(fp.read(), progname, 'exec')
globs = {
        '__file__': progname,
        '__name__': '__main__',
        '__package__': None,
        '__cached__': None,
        }
prof.runctx(code, globs, None)
prof.create_stats()
print(len(prof.stats))

我发现的第二种方法是,在执行脚本时删除所有已注册的模块。优点是,如果我在GUI运行时修改源代码,它将重新加载更改后的代码。但现在的缺点是,一些已注册的atexit处理程序会因为所需模块被删除而崩溃:
prof = Profile()

progname = 'myscript.py'
sys.path.insert(0, os.path.dirname(progname))
with open(progname, 'rb') as fp:
    code = compile(fp.read(), progname, 'exec')
globs = {
        '__file__': progname,
        '__name__': '__main__',
        '__package__': None,
        '__cached__': None,
        }
modules = sys.modules.copy()
prof.runctx(code, globs, None)
newmodes = [modname for modname in sys.modules if modname not in modules]
for modname in newmodes:
    del sys.modules[modname]
prof.create_stats()
print(len(prof.stats))

最后,我发现最好的方法是在单独的进程中执行分析:

import concurrent.futures
import marshal
from cProfile import Profile
from pstats import Stats
import sys

progname = 'myscript.py'
with concurrent.futures.ProcessPoolExecutor() as executor:
    future = executor.submit(_run, progname)
    stats = Stats()
    stats.stats = marshal.loads(future.result())
    stats.get_top_level_stats()

def _run(progname):
    sys.path.insert(0, os.path.dirname(progname))

    with open(progname, 'rb') as fp:
        code = compile(fp.read(), progname, 'exec')
    globs = {
        '__file__': progname,
        '__name__': '__main__',
        '__package__': None,
    }
    prof = Profile()
    prof.runctx(code, globs, None)
    prof.create_stats()
    return marshal.dumps(prof.stats)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接