看来,之所以函数调用次数不同的是,因为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)
我无法复制它,但是如果没有'myscript.py'中的代码就很困难。 – eandersson
这可能很遥远,但是有没有任何情况下你正在使用任何缓存装饰/记忆或类似的东西? (另外,我只使用了CPython,所以我不确定其他任何实现是否执行任何memoization/caching)。 – nvlass
我认为它与我正在分析的脚本中有'import'语句有关。 –