2013-04-15 67 views
3

我正在编写一个接口来帮助我分析python脚本。我借用代码来执行profile.py source code的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

+0

这可能很遥远,但是有没有任何情况下你正在使用任何缓存装饰/记忆或类似的东西? (另外,我只使用了CPython,所以我不确定其他任何实现是否执行任何memoization/caching)。 – nvlass

+0

我认为它与我正在分析的脚本中有'import'语句有关。 –

回答

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) 
相关问题