2009-02-16 7 views
126

Ich habe mich vor kurzem für Algorithmen interessiert und habe begonnen, sie zu erforschen, indem ich eine naive Implementierung schreibe und sie dann auf verschiedene Arten optimiere.Wie profiliere ich die Speichernutzung in Python?

Ich bin bereits mit dem Standard-Python-Modul für die Profilerstellung vertraut (für die meisten Dinge habe ich gefunden, dass die Zeitzauberfunktion in IPython ausreichend ist), aber ich bin auch daran interessiert, die Speicherauslastung zu erkunden Kompromisse (z. B. die Kosten für das Zwischenspeichern einer Tabelle zuvor berechneter Werte im Vergleich zu deren Neuberechnung nach Bedarf). Gibt es ein Modul, das den Speicherverbrauch einer bestimmten Funktion für mich profiliert?

Antwort

87

Dieser wurde bereits hier beantwortet: Python memory profiler

Grundsätzlich Sie tun so etwas (zitiert aus Guppy-PE):

>>> from guppy import hpy; h=hpy() 
>>> h.heap() 
Partition of a set of 48477 objects. Total size = 3265516 bytes. 
Index Count %  Size % Cumulative % Kind (class/dict of class) 
    0 25773 53 1612820 49 1612820 49 str 
    1 11699 24 483960 15 2096780 64 tuple 
    2 174 0 241584 7 2338364 72 dict of module 
    3 3478 7 222592 7 2560956 78 types.CodeType 
    4 3296 7 184576 6 2745532 84 function 
    5 401 1 175112 5 2920644 89 dict of class 
    6 108 0 81888 3 3002532 92 dict (no owner) 
    7 114 0 79632 2 3082164 94 dict of type 
    8 117 0 51336 2 3133500 96 type 
    9 667 1 24012 1 3157512 97 __builtin__.wrapper_descriptor 
<76 more rows. Type e.g. '_.more' to view.> 
>>> h.iso(1,[],{}) 
Partition of a set of 3 objects. Total size = 176 bytes. 
Index Count %  Size % Cumulative % Kind (class/dict of class) 
    0  1 33  136 77  136 77 dict (no owner) 
    1  1 33  28 16  164 93 list 
    2  1 33  12 7  176 100 int 
>>> x=[] 
>>> h.iso(x).sp 
0: h.Root.i0_modules['__main__'].__dict__['x'] 
>>> 
+3

Offizielle Guppy Dokumentation ist ein bisschen minimial; für andere Quellen siehe [dieses Beispiel] (http://smira.ru/wp-content/uploads/2011/08/heapy.html) und [der Haufen Essay] (http://guppy-pe.sourceforge.net/ Heapy-Thesis.pdf). – tutuDajuju

16

Für eine wirklich einfache Ansatz versuchen:

import resource 
def using(point=""): 
    usage=resource.getrusage(resource.RUSAGE_SELF) 
    return '''%s: usertime=%s systime=%s mem=%s mb 
      '''%(point,usage[0],usage[1], 
       (usage[2]*resource.getpagesize())/1000000.0) 

Fügen Sie einfach using("Label") ein, wo Sie sehen möchten, was vor sich geht.

+5

"Speichernutzung einer bestimmten Funktion", so dass Ihr Ansatz nicht hilft. – Glaslos

+0

Wenn Sie sich 'usage [2]' ansehen, betrachten Sie 'ru_maxrss', was nur der Teil des Prozesses ist, der * resident * ist. Dies hilft nicht viel, wenn der Prozess sogar teilweise auf die Festplatte ausgelagert wurde. – Louis

+3

'resource' ist ein Unix-spezifisches Modul, das nicht unter Windows funktioniert. – Martin

13

ich Sie nur auf der Speichernutzung eines Objekts suchen möchten, (answer to other question)

Es ist ein Modul Pympler, die enthält das asizeof Modul genannt.

Verwenden Sie wie folgt vor:

from pympler import asizeof 
asizeof.asizeof(my_object) 

Im Gegensatz zu sys.getsizeof, es für Ihre selbst erstellte Objekte funktioniert.

>>> asizeof.asizeof(tuple('bcd')) 
200 
>>> asizeof.asizeof({'foo': 'bar', 'baz': 'bar'}) 
400 
>>> asizeof.asizeof({}) 
280 
>>> asizeof.asizeof({'foo':'bar'}) 
360 
>>> asizeof.asizeof('foo') 
40 
>>> asizeof.asizeof(Bar()) 
352 
>>> asizeof.asizeof(Bar().__dict__) 
280 
>>> help(asizeof.asizeof) 
Help on function asizeof in module pympler.asizeof: 

asizeof(*objs, **opts) 
    Return the combined size in bytes of all objects passed as positional arguments. 
+0

Ist diese Größe mit RSS verwandt? – mousecoder

+0

Ist diese Größe in Bytes? – mousecoder

+1

@mousecoder: Ja, es ist in Bytes. Siehe Änderungsantrag –

7

Python 3.4 enthält ein neues Modul: tracemalloc. Es bietet detaillierte Statistiken darüber, welcher Code den meisten Speicher zuweist. Hier ist ein Beispiel, das die obersten drei Zeilen zeigt, die Speicher belegen.

from collections import Counter 
import linecache 
import os 
import tracemalloc 

def display_top(snapshot, key_type='lineno', limit=3): 
    snapshot = snapshot.filter_traces((
     tracemalloc.Filter(False, "<frozen importlib._bootstrap>"), 
     tracemalloc.Filter(False, "<unknown>"), 
    )) 
    top_stats = snapshot.statistics(key_type) 

    print("Top %s lines" % limit) 
    for index, stat in enumerate(top_stats[:limit], 1): 
     frame = stat.traceback[0] 
     # replace "/path/to/module/file.py" with "module/file.py" 
     filename = os.sep.join(frame.filename.split(os.sep)[-2:]) 
     print("#%s: %s:%s: %.1f KiB" 
       % (index, filename, frame.lineno, stat.size/1024)) 
     line = linecache.getline(frame.filename, frame.lineno).strip() 
     if line: 
      print(' %s' % line) 

    other = top_stats[limit:] 
    if other: 
     size = sum(stat.size for stat in other) 
     print("%s other: %.1f KiB" % (len(other), size/1024)) 
    total = sum(stat.size for stat in top_stats) 
    print("Total allocated size: %.1f KiB" % (total/1024)) 


tracemalloc.start() 

counts = Counter() 
fname = '/usr/share/dict/american-english' 
with open(fname) as words: 
    words = list(words) 
    for word in words: 
     prefix = word[:3] 
     counts[prefix] += 1 
print('Top prefixes:', counts.most_common(3)) 

snapshot = tracemalloc.take_snapshot() 
display_top(snapshot) 

Und hier sind die Ergebnisse:

Top prefixes: [('con', 1220), ('dis', 1002), ('pro', 809)] 
Top 3 lines 
#1: scratches/memory_test.py:37: 6527.1 KiB 
    words = list(words) 
#2: scratches/memory_test.py:39: 247.7 KiB 
    prefix = word[:3] 
#3: scratches/memory_test.py:40: 193.0 KiB 
    counts[prefix] += 1 
4 other: 4.3 KiB 
Total allocated size: 6972.1 KiB 
2

vielleicht ist es Hilfe:
< see additional>

pip install gprof2dot 
sudo apt-get install graphviz 

gprof2dot -f pstats profile_for_func1_001 | dot -Tpng -o profile.png 

def profileit(name): 
    """ 
    @profileit("profile_for_func1_001") 
    """ 
    def inner(func): 
     def wrapper(*args, **kwargs): 
      prof = cProfile.Profile() 
      retval = prof.runcall(func, *args, **kwargs) 
      # Note use of name from outer scope 
      prof.dump_stats(name) 
      return retval 
     return wrapper 
    return inner 

@profileit("profile_for_func1_001") 
def func1(...)