It seems the reason why the function call count is different is because modules imported by myscript.py
aren't imported again the second time I run the code.
A first way to get consistent results is to import myscript.py
before performing the profiling. However, it means that if modules I import perform some task when imported, this won't be profiled.
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))
The second way I found is to delete all modules registered when I executed my script. The advantage is that if I modify the source while my GUI is running, it will be reloaded with the changes. The drawback I have now is that some atexit registered handlers now crash because needed modules are deleted before:
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))
Finally, the best way I found is to execute the profiling in a separate process:
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)