>>50604Wir haben schon in
>>51361 für Problem 7 Primzahlen bis 200'000 gesiebt. Jetzt einfach denselben Generator für 2 Mio. erzeugen und dann
print(sum(P)) und fertig!
Das ist also auch noch nicht die Aufgabe, für die wir Bitvektoren brauchen werden. Nur, wann ist es soweit, dass unser Programm nicht mehr in den Speicher passt?
Mit
sys.getsizeof können wir schon einzelne Variablen untersuchen. Um das Programm insgesamt einzuschätzen, suchen wir den maximalen Speicherverbrauch. Leider unterscheiden sich die Befehle da nach Betriebssystem:
# Linux
from resource import getrusage, RUSAGE_SELF
# hier Algorithmus einfügen
print(getrusage(RUSAGE_SELF).ru_maxrss)
# Windows
import psutil
# Arbeit, Arbeit...
print(psutil.Process().memory_info().peak_wset)
Gegebenfalls muss man erstmal
pip install psutil bemühen. Dann hat man aber auch ein Modul, dessen Funktionsumfang weit über das hinausgeht, was wir hier brauchen.
Wenn man schon dabei ist, kann man sich auch den
memory_profile dazu installieren. Dann schreibst du
from memory_profiler import profile zu deinen Importen, packst deinen Algorithmus in eine Funktion und schreibst
@profile in die Zeile über das
def. Nach dem Ausführen bekommst du dann eine zeilenweise Übersicht über den Speicherbedarf.
Line # Mem usage Increment Line Contents
================================================
2 13.8 MiB 13.8 MiB @profile
3 def SumOfPrimes(MaxNum):
4 13.8 MiB 0.0 MiB Sieb = []
5
6 91.3 MiB 0.0 MiB for i in range(0, MaxNum+1):
7 91.3 MiB 77.5 MiB Sieb.append(i)
8
9 91.3 MiB 0.0 MiB Removers = [0, 1]
10 580.6 MiB 0.0 MiB for Prime in Sieb:
11 580.6 MiB 0.0 MiB if Prime!=0 and Prime!=1:
12 580.6 MiB 0.0 MiB Maex = int(MaxNum/Prime)
13 580.6 MiB 394.6 MiB for i in range(Prime, Maex+1):
14 580.6 MiB 94.6 MiB Removers.append(i*Prime)
15
16 291.6 MiB -289.0 MiB Removers = list(set(Removers))
17 305.5 MiB -7707477.4 MiB for i in sorted(Removers, reverse=True):
18 305.5 MiB -7707491.3 MiB del Sieb[i]
19 292.1 MiB -13.4 MiB return sum(Sieb)
Die Ausführung wird dadurch natürlich verlangsamt, also musst du die Laufzeit separat testen.
Dein Programm ist hierfür ein sehr dankbares Beispiel, weil du Listen dynamisch anfragst und andernorts Einträge wieder löschst. Damit wächst und schrumpft der Speicher, den dein Programm braucht, immer wieder.
Beim
del gibt es allerdings ein Problem, denn die angezeigte Speicherfreigabe (rund 7,7 TiB) in Zeile 17 und 18 ist Unsinn. Dein Programm war während der Ausführung tatsächlich nie größer als die angegeben 580 MiB.
Leistungsmessung ist anscheinend nicht immer einfach. Die 13,8 MiB vom Anfang sind übrigens der Grundbedarf, den Python und die Module haben. Ich habe dasselbe mal mit
print('Krtek') versucht und es waren auch 13,8 MiB.
Mit dem Paket
memory_profiler kommt außerdem ein Programm namens
mprof (oder
python3-mprof, je nachdem, wer das paketiert hat). Damit sagt man dann
mprof run SumOfPrimes.py
mprof plot
# `pip install matplotlib`, falls plot "No module named 'pylab'" sagt
Dann bekombt man seinen Speicherbedarf grafisch aufbereitet (so wie bsp.png).
Kühlerweise hat man dann Speicher und Ausführungszeit gegenübergestellt. Auch wenn die Programme insgesamt langsamer laufen, sieht das nach einem guten Anhaltspunkt aus, um die Komplexität seines Algorithmus überwachen.
Das nützt uns freilich nicht viel, wenn wir tatsächlich zu C oder einer anderen Sprache wechseln sollten. Zumindest unter Linux kann man versuchen, in der Sprache auf die
getrusage-API zuzugreifen oder man nutzt einfach GNU time (ggf. installieren).
\time -f '%M' python SumOfPrimes.py
Das \ vor dem
time ist wichtig, weil du sonst die eingebaute Funktion deiner Muschel aufrufst und die ist bei weitem nicht so mächtig. Interessant ist auch,
\time -v, das dir allerhand weitere Informationen gibt.
Unter Windows bekommt man die Information vermutlich mit
resmon.exe oder
perfmon.exe, aber damit habe ich mich noch nicht anfreunden können. Ich fand immer den Prozesserforscher (
procexp.exe) von Sysinternals kühler. Bei dem klickt man auf einen Prozess doppelt, wählt den Reiter "Performance", bei "Physical Memory" schaut man nach "Peak Working Set".
Wieder etwas dazugelernt. Dann kann Problem 41 bald kommen :^)
>>50604Wir haben schon in
>>51361 für Problem 7 Primzahlen bis 200'000 gesiebt. Jetzt einfach denselben Generator für 2 Mio. erzeugen und dann
print(sum(P)) und fertig!
Das ist also auch noch nicht die Aufgabe, für die wir Bitvektoren brauchen werden. Nur, wann ist es soweit, dass unser Programm nicht mehr in den Speicher passt?
Mit
sys.getsizeof können wir schon einzelne Variablen untersuchen. Um das Programm insgesamt einzuschätzen, suchen wir den maximalen Speicherverbrauch. Leider unterscheiden sich die Befehle da nach Betriebssystem:
# Linux
from resource import getrusage, RUSAGE_SELF
# hier Algorithmus einfügen
print(getrusage(RUSAGE_SELF).ru_maxrss)
[48 weitere Zeilen anzeigen]