Speicherverbrauch

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

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?
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Um den Speicherbedarf zu minimieren, arbeite die Zeilen zeilenweise ab:

Code: Alles auswählen

with open('foo') as fobj:
    for line in fobj: ...
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

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.
schorsch
User
Beiträge: 18
Registriert: Montag 26. November 2007, 18:39

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.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

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
Zuletzt geändert von sma am Sonntag 28. November 2010, 11:50, insgesamt 1-mal geändert.
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

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?
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

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
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@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.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

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
Antworten