2013-04-15 91 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) 
相關問題