Speicherverbrauch: Erstellen eines großen Sets gegen das Zusammenführen vieler kleiner Sets

8

Ich habe die magische Funktion %memit verwendet, um die Speichernutzung zu messen:

%Vor%

OK, es scheint also einen Zwischenschritt zu geben, in dem xrange(n) in eine vollständige Liste instanziiert wird. Aber was, wenn ich meine Liste in 10 Unterlisten zerschneide und sie einzeln einsammle? Dies wäre mehr Speichereffizienz, oder?

%Vor%

Nun, das lief nicht wie erwartet. Warum verbraucht der reduce -Ansatz mehr Speicher als set(xrange(n)) ?

    
usual me 05.09.2015, 12:47
quelle

2 Antworten

3

Es gibt viele Missverständnisse, die geklärt werden müssen. Zunächst wird ein xrange nicht in eine Liste umgewandelt, bevor es zu einem set in set(xrange(n)) gemacht wird!

Der Spitzenspeicher für den reduce -Ansatz ergibt sich aus der Tatsache, dass set.union einen neuen Wert zurückgibt, der eine Vereinigung der 2 Ergebnismengen ist. Und reduce speichert intern einen zusätzlichen Wert, den accum_value . Also auf der letzten Iteration dieser for Schleife:

%Vor%

Das accum_value , das in die Funktion einfließt, enthält 90% von 10⁷ Elementen, x enthält 10% von 10⁷ Elementen, aber der Rückgabewert benötigt Platz für alle 10⁷ Elemente. Raum für all diese Dinge muss gleichzeitig reserviert werden. Erst nachdem function zurückgegeben wurde, wird der Speicher für die alten accum_value und x freigegeben.

Aber das ist noch nicht das Ende. Der Spitzenspeicher gibt an, wie viel Speicher in benötigt wird, aber nicht, wie viel verwendet wird, nachdem der Vorgang abgeschlossen wurde, wenn der Satz noch existierte.

>

Somit wird ein anderer Benchmark benötigt:

%Vor%

und

%Vor%

Die Speicherauslastung für reduce fällt nach der Ausführung auf 778 MiB ab; es ist jedoch immer noch mehr als für den einfacheren Fall-Aufbau-Fall. Und wie Sie sehen können, benötigt der set(xrange(n)) nicht viel internen Speicher, da die Speicherauslastung nach der Erstellung des Sets um 9 MiB abfällt.

Vor allem ist der reduce -Ansatz nicht nur langsamer; Die resultierende Menge verbraucht außerdem doppelt so viel Speicher . Dies liegt daran, dass eine Menge durch eine Hashtabelle unterstützt wird und dass sie immer größer wird, wenn angenommen wird, dass sie zu viele Kollisionen aufweist. Sie haben pathologisches Verhalten festgestellt, bei dem die gleichen Werte dazu führen, dass einer doppelt so viel Speicher verbraucht wie der andere.

    
Antti Haapala 05.09.2015, 14:35
quelle
4

Pro% der Dokumente entspricht reduce in etwa:

%Vor%

Iterieren über iterable , (set(xrange(p, n, 10)) for p in range(10)) , benötigt ca. 447 MiB. Sie könnten denken, dass, da dieses iterable ein Generator-Ausdruck ist, Sie Speicher sparen werden, aber ganze Zahlen sind in einer internen freien Liste gespeichert :

  

"Für Geschwindigkeit" verwaltet Python eine interne freie Liste für Integer-Objekte. Leider ist diese freie Liste sowohl unsterblich als auch unbeschränkt.

Sobald also jeder Satz instanziiert wurde, wird der meiste Speicher, den er verbraucht, niemals freigegeben.

Der zurückgegebene Wert accum_value benötigt ebenfalls ca. 447 MiB. Zusammen erfordert der Aufruf von reduce also ungefähr 447 + 447 = 894 MiB.

    
unutbu 05.09.2015 13:02
quelle

Tags und Links