4 cze 2010

Profiler dla Pythona

Dzisiaj nieco tekstu technicznego. W swojej pracy miałem styczność z jboss-profilerem, czyli programem napisanym w Javie, a umożliwiającym podgląd efektywności napisanego kodu. Profiler ten, oprócz standardowych informacji o czasie wykonywania poszczególnych funkcji udostępniał dane o ilości alokacji poszczególnych obiektów. W Pythonie mamy co prawda cProfiler, który także generuje dane o czasach wykonania poszczególnych funkcji, jednak nie udostępnia on informacji o ilości instancji danego obiektu oraz rozmiarze pamięci zajmowanej przez obiekt. Oto moje rozwiązanie, czyli profiler monitorujący za pomocą modułu Garbage Collectora liczbę zaalokowanych i nie zwolnionych obiektów w pamięci:


import sys
import os
import gc
import types
import inspect
import atexit

def check_allocations():
dt = {}
#gc.collect() jeśli byśmy chcieli info tylko o obiektach żyjących
for obj in gc.get_objects():
try:
if dt.has_key(obj.__class__.__name__):
instances,number,size,typ,bi,module = dt[obj.__class__.__name__]
number+=1
size+=sys.getsizeof(obj)
if obj not in instances:
instances.append(obj)
dt[obj.__class__.__name__] = (instances,number,size,typ,bi,module)
else:
dt[obj.__class__.__name__] = ([obj],1,sys.getsizeof(obj),type(obj),inspect.isbuiltin(obj),obj.__module__)
except AttributeError as e:
pass
return dt

def run():
data = check_allocations()
print '\n\nCurrent allocations'
print '\t%40s | %9s | %9s bytes | <<%s>>' % ('Class name', 'number', 'size', 'module')
print '\t-----------------------------------------------------------------------------------'
for key in data:
instcs,number,size,typ,bi,module = data[key]
if module == '__main__':
print '\t%-40.40s | %9d | %9d bytes | <<%s>>' % (key, number, size, module)
for inst in instcs:
print '\t -> %14.14s 0x%016x | %9s | %9s bytes | <<%s>>' % ('id', id(inst),' ',sys.getsizeof(inst),'---')

print gc.garbage
print '\n\n'

def main():
import os, sys
from optparse import OptionParser
usage = "profiler.py scriptfile"
parser = OptionParser(usage=usage)
parser.allow_interspersed_args = False

if not sys.argv[1:]:
parser.print_usage()
sys.exit(2)
(options,args) = parser.parse_args()

sys.argv[:] = args

if len(sys.argv) > 0:
import __main__
dict = __main__.__dict__

sys.path.insert(0, os.path.dirname(sys.argv[0]))
gc.set_threshold(0)
gc.set_debug(gc.DEBUG_SAVEALL)
try:
exec 'execfile(%r)' % sys.argv[0] in dict, dict
except KeyboardInterrupt as ki:
pass
atexit.register(run)
else:
parser.print_usage()

if __name__ == '__main__':
main()



Kompletny kod jest dostępny tutaj.

Powyższy kod ma obecnie tylko i wyłącznie wartość demonstracyjną, może służyć do badania małych skryptów. Jeśli chcielibyśmy podglądać większe programy, warto w funkcji run ustawić filtrowanie modułów na te, które chcemy monitorować.

Korzystanie z mojego profilera jest analogiczne do tego z cProfilera:


python -m profiler monitorowany_skrypt.py


Miłego korzystania ;)

0 komentarze:

Prześlij komentarz