Wie profiliere ich die Speichernutzung in Python?

230

Ich habe mich kürzlich für Algorithmen interessiert und begonnen, sie zu untersuchen, indem ich eine naive Implementierung geschrieben und sie dann auf verschiedene Arten optimiert habe.

Ich bin bereits mit dem Standard-Python-Modul für die Profilerstellung zur Laufzeit vertraut (für die meisten Dinge habe ich festgestellt, dass die Timeit Magic-Funktion in IPython ausreichend ist), aber ich bin auch an der Speichernutzung interessiert, damit ich auch diese Kompromisse untersuchen kann ( z. B. die Kosten für das Zwischenspeichern einer Tabelle zuvor berechneter Werte im Vergleich zur erneuten Berechnung nach Bedarf). Gibt es ein Modul, das die Speichernutzung einer bestimmten Funktion für mich erfasst?

Lawrence Johnston
quelle
Duplikat Welcher Python-Speicherprofiler wird empfohlen? . IMHO beste Antwort im Jahr 2019 ist memory_profiler
vladkha

Antworten:

118

Dieser wurde bereits hier beantwortet: Python Memory Profiler

Grundsätzlich machen Sie so etwas (zitiert nach 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']
>>> 
Hubert
quelle
6
Die offizielle Guppy-Dokumentation ist etwas minimal; Weitere Ressourcen finden Sie in diesem Beispiel und im ausführlichen Aufsatz .
TutuDajuju
13
Guppy scheint nicht mehr gewartet zu werden, daher schlage ich vor, diese Antwort herabzustufen und stattdessen eine der anderen Antworten zu akzeptieren.
Robguinness
1
@robguinness Mit herabgestuft meinst du herabgestimmt? Das scheint nicht fair zu sein, weil es zu einem bestimmten Zeitpunkt wertvoll war. Ich denke, eine Bearbeitung oben besagt, dass sie aus X-Gründen nicht mehr gültig ist und stattdessen die Antwort Y oder Z angezeigt wird. Ich halte diese Vorgehensweise für angemessener.
WinEunuuchs2Unix
1
Sicher, das funktioniert auch, aber irgendwie wäre es schön, wenn die akzeptierte und am höchsten bewertete Antwort eine Lösung beinhalten würde, die immer noch funktioniert und beibehalten wird.
Robguinness
92

Python 3.4 enthält ein neues Modul : tracemalloc. Es enthält detaillierte Statistiken darüber, welcher Code den meisten Speicher zuweist. In diesem Beispiel werden die drei obersten Zeilen angezeigt, in denen Speicher zugewiesen wird.

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

Wann ist ein Speicherverlust kein Leck?

Dieses Beispiel ist großartig, wenn der Speicher am Ende der Berechnung noch gehalten wird, aber manchmal haben Sie Code, der viel Speicher zuweist und dann alles freigibt. Es ist technisch gesehen kein Speicherverlust, aber es verbraucht mehr Speicher als Sie denken. Wie können Sie die Speichernutzung verfolgen, wenn alles freigegeben wird? Wenn es sich um Ihren Code handelt, können Sie wahrscheinlich Debugging-Code hinzufügen, um während der Ausführung Snapshots zu erstellen. Wenn nicht, können Sie einen Hintergrundthread starten, um die Speichernutzung zu überwachen, während der Hauptthread ausgeführt wird.

Hier ist das vorherige Beispiel, in dem der gesamte Code in die count_prefixes()Funktion verschoben wurde . Wenn diese Funktion zurückkehrt, wird der gesamte Speicher freigegeben. Ich habe auch einige sleep()Aufrufe hinzugefügt , um eine langfristige Berechnung zu simulieren.

from collections import Counter
import linecache
import os
import tracemalloc
from time import sleep


def count_prefixes():
    sleep(2)  # Start up time.
    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
            sleep(0.0001)
    most_common = counts.most_common(3)
    sleep(3)  # Shut down time.
    return most_common


def main():
    tracemalloc.start()

    most_common = count_prefixes()
    print('Top prefixes:', most_common)

    snapshot = tracemalloc.take_snapshot()
    display_top(snapshot)


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))


main()

Wenn ich diese Version ausführe, ist die Speichernutzung von 6 MB auf 4 KB gesunken, da die Funktion nach Beendigung den gesamten Speicher freigegeben hat.

Top prefixes: [('con', 1220), ('dis', 1002), ('pro', 809)]
Top 3 lines
#1: collections/__init__.py:537: 0.7 KiB
    self.update(*args, **kwds)
#2: collections/__init__.py:555: 0.6 KiB
    return _heapq.nlargest(n, self.items(), key=_itemgetter(1))
#3: python3.6/heapq.py:569: 0.5 KiB
    result = [(key(elem), i, elem) for i, elem in zip(range(0, -n, -1), it)]
10 other: 2.2 KiB
Total allocated size: 4.0 KiB

Hier ist eine Version, die von einer anderen Antwort inspiriert wurde und einen zweiten Thread zur Überwachung der Speichernutzung startet.

from collections import Counter
import linecache
import os
import tracemalloc
from datetime import datetime
from queue import Queue, Empty
from resource import getrusage, RUSAGE_SELF
from threading import Thread
from time import sleep

def memory_monitor(command_queue: Queue, poll_interval=1):
    tracemalloc.start()
    old_max = 0
    snapshot = None
    while True:
        try:
            command_queue.get(timeout=poll_interval)
            if snapshot is not None:
                print(datetime.now())
                display_top(snapshot)

            return
        except Empty:
            max_rss = getrusage(RUSAGE_SELF).ru_maxrss
            if max_rss > old_max:
                old_max = max_rss
                snapshot = tracemalloc.take_snapshot()
                print(datetime.now(), 'max RSS', max_rss)


def count_prefixes():
    sleep(2)  # Start up time.
    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
            sleep(0.0001)
    most_common = counts.most_common(3)
    sleep(3)  # Shut down time.
    return most_common


def main():
    queue = Queue()
    poll_interval = 0.1
    monitor_thread = Thread(target=memory_monitor, args=(queue, poll_interval))
    monitor_thread.start()
    try:
        most_common = count_prefixes()
        print('Top prefixes:', most_common)
    finally:
        queue.put('stop')
        monitor_thread.join()


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))


main()

Mit dem resourceModul können Sie die aktuelle Speichernutzung überprüfen und den Snapshot von der maximalen Speichernutzung speichern. In der Warteschlange kann der Hauptthread dem Speichermonitor-Thread mitteilen, wann sein Bericht gedruckt und heruntergefahren werden soll. Wenn es ausgeführt wird, wird der vom list()Aufruf verwendete Speicher angezeigt:

2018-05-29 10:34:34.441334 max RSS 10188
2018-05-29 10:34:36.475707 max RSS 23588
2018-05-29 10:34:36.616524 max RSS 38104
2018-05-29 10:34:36.772978 max RSS 45924
2018-05-29 10:34:36.929688 max RSS 46824
2018-05-29 10:34:37.087554 max RSS 46852
Top prefixes: [('con', 1220), ('dis', 1002), ('pro', 809)]
2018-05-29 10:34:56.281262
Top 3 lines
#1: scratches/scratch.py:36: 6527.0 KiB
    words = list(words)
#2: scratches/scratch.py:38: 16.4 KiB
    prefix = word[:3]
#3: scratches/scratch.py:39: 10.1 KiB
    counts[prefix] += 1
19 other: 10.8 KiB
Total allocated size: 6564.3 KiB

Wenn Sie unter Linux arbeiten, ist dies möglicherweise /proc/self/statmnützlicher als das resourceModul.

Don Kirkby
quelle
Das ist großartig, aber es scheint die Snapshots nur in Intervallen zu drucken, wenn Funktionen innerhalb von "count_prefixes ()" zurückkehren. Mit anderen Worten, wenn Sie einen lang laufenden Anruf haben, z. B. long_running()innerhalb der count_prefixes()Funktion, werden die maximalen RSS-Werte erst gedruckt, wenn sie long_running()zurückgegeben werden. Oder irre ich mich?
Robguinness
Ich denke du liegst falsch, @robguinness. memory_monitor()wird in einem separaten Thread von ausgeführt count_prefixes(). Die einzigen Möglichkeiten, wie sich einer auf den anderen auswirken kann, sind die GIL und die Nachrichtenwarteschlange, an die ich übergebe memory_monitor(). Ich vermute, dass beim count_prefixes()Aufrufen sleep()der Thread-Kontext zum Wechseln angeregt wird. Wenn long_running()es nicht sehr lange dauert, wechselt der Thread-Kontext möglicherweise erst, wenn Sie den sleep()Rückruf erneut ausführen count_prefixes(). Wenn das keinen Sinn ergibt, poste eine neue Frage und verlinke sie von hier aus.
Don Kirkby
Vielen Dank. Ich werde eine neue Frage stellen und hier einen Link hinzufügen. (Ich muss ein Beispiel für das Problem
ausarbeiten,
31

Wenn Sie nur die Speichernutzung eines Objekts betrachten möchten ( Antwort auf eine andere Frage )

Es gibt ein Modul namens Pympler, das das asizeof Modul enthält .

Verwenden Sie wie folgt:

from pympler import asizeof
asizeof.asizeof(my_object)

Im Gegensatz sys.getsizeofdazu funktioniert es für Ihre selbst erstellten Objekte .

>>> 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.
serv-inc
quelle
1
Bezieht sich diese Größe auf RSS?
pg2455
1
@mousecoder: Welches RSS unter en.wikipedia.org/wiki/RSS_(disambiguation) ? Web-Feeds? Wie?
serv-inc
2
@ serv-inc Resident Set Größe , obwohl ich nur eine Erwähnung in Pymplers Quelle finden kann und diese Erwähnung nicht direkt damit verbunden zu sein scheintasizeof
jkmartindale
1
@mousecoder Der von gemeldete Speicher asizeofkann zu RSS beitragen, ja. Ich bin mir nicht sicher, was Sie sonst noch mit "verwandt mit" meinen.
OrangeDog
1
@ serv-inc es ist möglich, dass es sehr fallspezifisch sein kann. aber für meinen Anwendungsfall, der ein großes mehrdimensionales Wörterbuch misst, fand ich eine tracemallocLösung unterhalb einer Größenordnung schneller
ulkas
22

Offenlegung:

  • Gilt nur unter Linux
  • Meldet den vom aktuellen Prozess als Ganzes verwendeten Speicher und nicht einzelne Funktionen innerhalb

Aber schön wegen seiner Einfachheit:

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]/1024.0 )

Fügen using("Label")Sie einfach ein, wo Sie sehen möchten, was los ist. Beispielsweise

print(using("before"))
wrk = ["wasting mem"] * 1000000
print(using("after"))

>>> before: usertime=2.117053 systime=1.703466 mem=53.97265625 mb
>>> after: usertime=2.12023 systime=1.70708 mem=60.8828125 mb
anon
quelle
6
"Speichernutzung einer bestimmten Funktion", daher hilft Ihr Ansatz nicht.
Glaslos
Wenn usage[2]Sie sich ansehen ru_maxrss, sehen Sie , was nur der Teil des Prozesses ist, der resident ist . Dies hilft nicht viel, wenn der Prozess auch nur teilweise auf die Festplatte übertragen wurde.
Louis
8
resourceist ein Unix-spezifisches Modul, das unter Windows nicht funktioniert.
Martin
1
Die Einheiten von ru_maxrss(d. H. usage[2]) Sind kB, keine Seiten, sodass diese Zahl nicht mit multipliziert werden muss resource.getpagesize().
Tey '
1
Dies druckte nichts für mich aus.
Quantumpotato
7

Da die akzeptierte Antwort und auch die Antwort mit der nächsthöheren Stimme meiner Meinung nach einige Probleme haben, möchte ich eine weitere Antwort anbieten, die eng auf der Antwort von Ihor B. basiert, mit einigen kleinen, aber wichtigen Änderungen.

Mit dieser Lösung können Sie die Profilerstellung ausführen, indem Sie entweder einen Funktionsaufruf mit der profileFunktion umschließen und aufrufen oder Ihre Funktion / Methode mit dem @profileDekorator dekorieren .

Die erste Technik ist nützlich, wenn Sie Code von Drittanbietern profilieren möchten, ohne die Quelle zu beeinträchtigen, während die zweite Technik etwas "sauberer" ist und besser funktioniert, wenn es Ihnen nichts ausmacht, die Quelle der von Ihnen verwendeten Funktion / Methode zu ändern Profil erstellen wollen.

Ich habe auch die Ausgabe so geändert, dass Sie RSS, VMS und gemeinsam genutzten Speicher erhalten. Die "Vorher" - und "Nachher" -Werte interessieren mich nicht sehr, sondern nur das Delta, also habe ich diese entfernt (wenn Sie mit der Antwort von Ihor B. vergleichen).

Profiling-Code

# profile.py
import time
import os
import psutil
import inspect


def elapsed_since(start):
    #return time.strftime("%H:%M:%S", time.gmtime(time.time() - start))
    elapsed = time.time() - start
    if elapsed < 1:
        return str(round(elapsed*1000,2)) + "ms"
    if elapsed < 60:
        return str(round(elapsed, 2)) + "s"
    if elapsed < 3600:
        return str(round(elapsed/60, 2)) + "min"
    else:
        return str(round(elapsed / 3600, 2)) + "hrs"


def get_process_memory():
    process = psutil.Process(os.getpid())
    mi = process.memory_info()
    return mi.rss, mi.vms, mi.shared


def format_bytes(bytes):
    if abs(bytes) < 1000:
        return str(bytes)+"B"
    elif abs(bytes) < 1e6:
        return str(round(bytes/1e3,2)) + "kB"
    elif abs(bytes) < 1e9:
        return str(round(bytes / 1e6, 2)) + "MB"
    else:
        return str(round(bytes / 1e9, 2)) + "GB"


def profile(func, *args, **kwargs):
    def wrapper(*args, **kwargs):
        rss_before, vms_before, shared_before = get_process_memory()
        start = time.time()
        result = func(*args, **kwargs)
        elapsed_time = elapsed_since(start)
        rss_after, vms_after, shared_after = get_process_memory()
        print("Profiling: {:>20}  RSS: {:>8} | VMS: {:>8} | SHR {"
              ":>8} | time: {:>8}"
            .format("<" + func.__name__ + ">",
                    format_bytes(rss_after - rss_before),
                    format_bytes(vms_after - vms_before),
                    format_bytes(shared_after - shared_before),
                    elapsed_time))
        return result
    if inspect.isfunction(func):
        return wrapper
    elif inspect.ismethod(func):
        return wrapper(*args,**kwargs)

Beispiel für die Verwendung unter der Annahme, dass der obige Code wie folgt gespeichert ist profile.py:

from profile import profile
from time import sleep
from sklearn import datasets # Just an example of 3rd party function call


# Method 1
run_profiling = profile(datasets.load_digits)
data = run_profiling()

# Method 2
@profile
def my_function():
    # do some stuff
    a_list = []
    for i in range(1,100000):
        a_list.append(i)
    return a_list


res = my_function()

Dies sollte zu einer Ausgabe ähnlich der folgenden führen:

Profiling:        <load_digits>  RSS:   5.07MB | VMS:   4.91MB | SHR  73.73kB | time:  89.99ms
Profiling:        <my_function>  RSS:   1.06MB | VMS:   1.35MB | SHR       0B | time:   8.43ms

Ein paar wichtige Schlussbemerkungen:

  1. Beachten Sie, dass diese Methode zur Profilerstellung nur annähernd ist, da möglicherweise viele andere Dinge auf dem Computer passieren. Aufgrund der Speicherbereinigung und anderer Faktoren können die Deltas sogar Null sein.
  2. Aus einem unbekannten Grund werden sehr kurze Funktionsaufrufe (z. B. 1 oder 2 ms) ohne Speicherauslastung angezeigt. Ich vermute, dies ist eine Einschränkung der Hardware / des Betriebssystems (getestet auf einem einfachen Laptop unter Linux) hinsichtlich der Häufigkeit, mit der Speicherstatistiken aktualisiert werden.
  3. Um die Beispiele einfach zu halten, habe ich keine Funktionsargumente verwendet, aber sie sollten wie erwartet funktionieren, dh profile(my_function, arg)profilierenmy_function(arg)
Robguinness
quelle
7

Im Folgenden finden Sie einen einfachen Funktionsdekorator, mit dem Sie nachverfolgen können, wie viel Speicher der Prozess vor dem Funktionsaufruf, nach dem Funktionsaufruf verbraucht hat und was der Unterschied ist:

import time
import os
import psutil


def elapsed_since(start):
    return time.strftime("%H:%M:%S", time.gmtime(time.time() - start))


def get_process_memory():
    process = psutil.Process(os.getpid())
    return process.get_memory_info().rss


def profile(func):
    def wrapper(*args, **kwargs):
        mem_before = get_process_memory()
        start = time.time()
        result = func(*args, **kwargs)
        elapsed_time = elapsed_since(start)
        mem_after = get_process_memory()
        print("{}: memory before: {:,}, after: {:,}, consumed: {:,}; exec time: {}".format(
            func.__name__,
            mem_before, mem_after, mem_after - mem_before,
            elapsed_time))
        return result
    return wrapper

Hier ist mein Blog, der alle Details beschreibt. ( archivierter Link )

Ihor B.
quelle
4
Zumindest in Ubuntu und Python 3.6 sollte dies process.memory_info().rssnicht der Fall sein process.get_memory_info().rss. verbunden stackoverflow.com/questions/41012058/psutil-error-on-macos
jangorecki
1
Sie haben Recht mit 3.x. Mein Kunde verwendet Python 2.7, nicht die neueste Version.
Ihor B.
4

Vielleicht hilft es:
< siehe zusätzliche >

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(...)
Madjardi
quelle
1

Ein einfaches Beispiel zur Berechnung der Speichernutzung eines Blocks von Codes / Funktionen mithilfe von memory_profile, während das Ergebnis der Funktion zurückgegeben wird:

import memory_profiler as mp

def fun(n):
    tmp = []
    for i in range(n):
        tmp.extend(list(range(i*i)))
    return "XXXXX"

Berechnen Sie die Speichernutzung, bevor Sie den Code ausführen, und berechnen Sie dann die maximale Nutzung während des Codes:

start_mem = mp.memory_usage(max_usage=True)
res = mp.memory_usage(proc=(fun, [100]), max_usage=True, retval=True) 
print('start mem', start_mem)
print('max mem', res[0][0])
print('used mem', res[0][0]-start_mem)
print('fun output', res[1])

Berechnen Sie die Verwendung in Stichprobenpunkten während der Ausführung der Funktion:

res = mp.memory_usage((fun, [100]), interval=.001, retval=True)
print('min mem', min(res[0]))
print('max mem', max(res[0]))
print('used mem', max(res[0])-min(res[0]))
print('fun output', res[1])

Credits: @skeept

nremenyi
quelle