Seite 1 von 1

Speicherverbrauch

Verfasst: Freitag 26. November 2010, 21:05
von ravenheart
Es geht um das gleich vorwegzunehmen immer noch darum, dass ich csv-artige Dateien bearbeiten möchte.
Ich spiele mit dem Gedanken, diese einfach komplett als String einzulesen und eventuell dann zeilenweise(wenn es geht) zu bearbeiten.
Ich hab mal folgendes getestet:

Code: Alles auswählen

f = open("hello.txt")
s = f.read()
s = s.split("\n")
i = 0
for line in s:
  print line
f.close()
hello.txt hab ich variabel erzeugt

Code: Alles auswählen

Größe von hello.txt in MB                   Anzahl an Zeilen(gerundet)                        Speicherverbrauch in MB (etwa)
       30,6                                             178000                                        40 
       166,6                                           2030000                                        300
       847,7                                         100000000                                        6000+ (geht in swap-Bereich)


Kann mir jmd erklären wieso die letzte Dateikonstellation so viel mehr Speicher benötigt?

Re: Speicherverbrauch

Verfasst: Freitag 26. November 2010, 21:20
von jbs
Um den Speicherbedarf zu minimieren, arbeite die Zeilen zeilenweise ab:

Code: Alles auswählen

with open('foo') as fobj:
    for line in fobj: ...

Re: Speicherverbrauch

Verfasst: Freitag 26. November 2010, 21:21
von nomnom
ravenheart hat geschrieben:

Code: Alles auswählen

f = open("hello.txt")
s = f.read()
s = s.split("\n")
i = 0
for line in s:
  print line
f.close()
Ginge einfacher:

Code: Alles auswählen

with open("hello.txt") as f:
    s = f.readlines()

for line in s:
    print line
Aber zu den Speicherproblemen kann ich nichts sagen. Soll nur ein Hinweis auf `with` und f.readlines() sein.

Re: Speicherverbrauch

Verfasst: Freitag 26. November 2010, 21:55
von schorsch
Ich würde eher zu jbs Version raten ;)

Der Sprung im Speicherverbrauch hängt mit der Zeilenlänge zusammen. Bei den ersten beiden Dateien ist sie recht großzüzgig, bei der letzten hingegen sehr klein. Nun liest du jede Zeile als einzelnen String ein und jedesmal kommt der Basisverbrauch eines Strings hinzu (also das was der String ohne die Zeichen selbst Verbrauch, bei mir 22 Byte, ich weiß nicht ob sich die Implementierungen da unterscheiden), was dann bei der kurzen länge der Strings stärker ins Gewicht fällt, als bei den anderen Dateien. Du kannst ja mal durchrechnen ob das hin kommt.

edit: Mit fällt gerade ein, dass ich irgendwo mal gelesen habe, dass man __sizeof__ nicht trauen kann. Also ist meine Angabe mit vorsicht zu genießen.

Re: Speicherverbrauch

Verfasst: Samstag 27. November 2010, 11:21
von Leonidas
schorsch hat geschrieben:Ich würde eher zu jbs Version raten ;)
Zudem sie auch nicht das obsolete ``readlines`` verwendet, das hier in dem Kontext auch überflüssig ist:

Code: Alles auswählen

with open('hello.txt', 'r') as f:
    for line in f:
        print line

Re: Speicherverbrauch

Verfasst: Samstag 27. November 2010, 12:51
von sma
Ich tippe darauf, dass die Kapazität der Liste, die bei `split()` entsteht, sprunghaft steigt, etwa in dem sie immer verdoppelt wird. Das kann bei vielen Einträgen dann so einem recht großen Overhead führen. Egal wie viel Speicher ein Rechner nun hat, ich finde es immer besser, Daten stromartig zu verarbeiten, wenn das möglich ist, also die Datei Zeile für Zeile zu lesen. Sollte das CSV-Modul das nicht können, muss man eben schnell selbst jeden String zerhacken.

Code: Alles auswählen

import re
s = "ich, sage, 'Hallo, Welt'"
print([m.group(2) or m.group(1) for m in re.finditer(r"\s*(?:'([^']*)'|([^,]+))", s)])
Stefan

Re: Speicherverbrauch

Verfasst: Samstag 27. November 2010, 14:24
von ravenheart
Ok, ich hab noch ein paar Spielereien gemacht

Code: Alles auswählen

f = open("test.txt")
string   = f.read()
strings = string.split("\n")
lines     = f.readlines() 
Ergebnis von sys.getsizeof():

Code: Alles auswählen

string    strings   lines
32077736  1585792   1486008
Wieso verbraucht der string 21 mal so viel Speicher?

Re: Speicherverbrauch

Verfasst: Samstag 27. November 2010, 14:59
von Barabbas
getsizeof() ermittelt nicht die Größe der Objekte, die die Liste referenziert, sondern nur die Größe des Listen-Objekts selbst.

Code: Alles auswählen

sum(sys.getsizeof(i) for i in strings)
gibt dir zusätzlich die Größe der referenzierten Objekte. Die Liste mit Strings ist hier also größer als ein großer String: Jedes weitere String-Objekt bringt einen Overhead von 24 Byte-32 Byte (Python < 3 und >= 3 unterscheiden sich hier) und die Liste selbst muss natürlich auch noch im Speicher aufbewahrt werden.

Das alles hat aber mit dem Iterieren nichts zu tun: Dabei befindet sich ja nie ein komplettes Abbild der ganzen Datei im Speicher, so dass das die beste Variante für dich sein dürfte.

Besten Gruß,

brb

Re: Speicherverbrauch

Verfasst: Samstag 27. November 2010, 22:50
von snafu
@sma: Wobei man das IMHO noch recht gut mit Pythons String-Methoden hinkriegen kann:

Code: Alles auswählen

>>> [x.strip('\'" ') for x in s.split(',')]
['ich', 'sage', 'Hallo Welt']
Überflüssig zu erwähnen, dass man bei Weiterverwendung der Elemente natürlich besser eine Generator Comprehension verwenden werden sollte.

Re: Speicherverbrauch

Verfasst: Sonntag 28. November 2010, 11:50
von sma
Der reguläre Ausdruck ist so kompliziert, weil ich ein Komma in einem String wie bei 'Hallo, Welt' als ein Element parsen muss. Einfach nur split reicht nämlich gerade nicht. Dummerweise hatte ich das in meinem Beispiel nicht berücksichtigt.

Stefan