Memory-Error

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.
Peak_me
User
Beiträge: 92
Registriert: Sonntag 27. Januar 2008, 03:09

huhu!

Als ich gerade im Taskmanager verzückt beobachtet habe, wie mein Programm die Arbeitsspeicherauslastung mit etwa 200 MB/s ansteigen ließ, kam die Prozedur aber bei 1,81 GB zum Stillstand und bei Python kam die Fehlermeldung:
y.append(x)
MemoryError
Heißt das, dass die Liste y zu lang geworden ist?
Oder war die Variable einfach zu groß für meinen Arbeitsspeicher? Ich hab 2 GB drin...

Die Liste, um die es geht, soll nach ihrer Erstellung in eine Datei geschrieben werden.
Ich habe die Riesenliste jetzt einfach in mehrere Teillisten aufgeteilt, die dann nacheinander in die Datei geschrieben werden.
Gibt es noch eine andere Möglichkeit?


Gruß
Paul
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Arbeite mit Streams in Form von Generatoren statt einer Liste.
Peak_me
User
Beiträge: 92
Registriert: Sonntag 27. Januar 2008, 03:09

Problem:
y=x.read()
MemoryError
Ich kann die Datei noch mit

Code: Alles auswählen

x = open("Z:\\bla.txt")
öffnen, doch kann sie dann nicht mehr weiterverarbeiten, da immer ein Memory-Error kommt.
Gibt es außer neuen Arbeitsspeicher kaufen noch eine andere Möglichkeit?
Ich hab grad keine Lust, tausende Einzeldatein anzulegen...
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Du versuchst die ganze Datei in einem Rutsch zu lesen. Das ist bei 1,8 GB natürlich etwas.... übertrieben. Versuch es mal mit dem optionalen Parameter, den read() kennt: http://docs.python.org/library/stdtypes ... le-objects
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Der MemoryError kommt, weil der restliche Speicher eben schon belegt ist, also solltest du nach Moeglichkeit mit Streams arbeiten, also Generatoren u.ae.
Wenn du die Daten aber komplett brauchst um sie zu bearbeiten, fuehrt wohl kein Weg an mehr Speicher vorbei, es koennte aber reichen, wenn das OS dem python-Prozess einfach mehr zuweist.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Peak_me hat geschrieben:Heißt das, dass die Liste y zu lang geworden ist?
Oder war die Variable einfach zu groß für meinen Arbeitsspeicher? Ich hab 2 GB drin...
Python geht bei Listen wie die meisten Sprachen vor und (in etwa) verdoppelt jeweils die Kapazität der Liste, wenn sie nicht mehr ausreicht. Angenommen, du hattest da 250.000.000 Einträge drin (was bei 4 Byte pro Eintrag 1GB ausmacht), dann würde Python versuchen, die Liste auf 325.000.000 Einträge zu vergrößern. Das kostet nicht nur 1,3GB, sondern auch 1,3GB extra, weil ja für einen kurzen Moment die alte und die neue Liste im Speichern sein müssen, damit die alten Elemente kopiert werden können.

Daher ist es nie eine gute Idee, große Listen zu benutzen. In anderen Sprachen (Java z.B.) kann man zumindest die Kapazität der Listen angeben, um dieses grenzenlose Wachstum zu vermeiden, doch Python erlaubt dies nicht.

Stefan
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Ich versuche mal eine mutige Vermutung (ja der Satz ist so gewollt)

32 Bit
Windows Betriebssystem
2 GB Virtuel Addressspace
Listen werden zum VA des Prozesses gezählt

Nur meine Würstchen mit Senf.

Grüße

Stefan
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

PyList_New() aus der C-API erlaubt die Angabe der Länge. Man könnte hier doch sicher eine Erweiterung implementieren oder es über `ctypes.pydll` probieren, was ein PyDLL-Objekt zum Nutzen der Python-API zurückliefert. Als fertiges Modul klingt hier blist erstmal ganz gut, nur leider haut mir das beim Kompilieren tausend Fehler um die Ohren.
BlackJack

@Peak_me: Wenn Du ein 32-Bit-Betriebssystem und ein 32-Bit-Python verwendest wird Dir auch mehr Arbeitsspeicher nicht viel nützen, denn auch wenn man mehr als 2 GiB da reinsteckt, ist 2 GiB in der Regel trotzdem die Obergrenze für den Speicher, den ein einzelner Prozess ansprechen kann.

Python's Implementierung von Listen spielt glaube ich auch keine grosse Rolle, wenn die Ausgangsdatei nicht einmal als ungeparste Binärdaten komplett in den Speicher passt.

Was machst Du mit diesen Daten denn? Müssen die den komplett im Speicher liegen? Musst Du so riesengrosse Listen im Speicher ansammeln? Wie schon mehrfach vorgeschlagen: Kann man die Verarbeitung nicht so formulieren, dass sie auf Datenströmen operiert und damit den benötigten Arbeitsspeicher minimieren!?

Ansonsten sind Daten die den Hauptspeicher sprengen, aber trotzdem wahlfreien Zugriff benötigen, oft ein Hinweis darauf, dass eine Datenbank eventuell Sinn macht.
Peak_me
User
Beiträge: 92
Registriert: Sonntag 27. Januar 2008, 03:09

Du versuchst die ganze Datei in einem Rutsch zu lesen. Das ist bei 1,8 GB natürlich etwas.... übertrieben. Versuch es mal mit dem optionalen Parameter, den read() kennt: http://docs.python.org/library/stdtypes ... le-objects
Nachdem ich die Datei erstellt habe, muss ich eigentlich nur an einzelne Zeilen ran, zB an Zeile 500.000.
Als einzig passendes in dem Link habe ich
file.readline([size])
gefunden, komm damit aber nicht klar, weil ich die Beschreibung nicht verstehe.

Das

Code: Alles auswählen

x=y.readline()
print x
liefert die erste Zeile der Datei.

Und das

Code: Alles auswählen

x=y.readline()
x=y.readline()
print x
liefert die zweite Zeile.

Wie kann ich Zeile 500.000 auslesen?
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Beim Zeilen-Auslesen bringt der der size-Parameter leider nichts - wie zuvor schon einige Leute gesagt haben, musst du über die Datei iterieren. Ich würde das vll. so lösen:

Code: Alles auswählen

line_counter = 0
for line in open("file", "r"):
    line_counter += 1
    if line_counter == 500000:
        print(line),
Vll. gibt es da noch bessere Lösungen, aber das sollte das Speicherproblem lösen.

Ich glaube nicht, dass es möglich ist, eine bestimmte Zeile auszulesen, ohne auch die vorherigen Zeilen "angefasst" zu haben. Aber man muss die unnützen Zeilen ja nicht im Speicher ablegen.

Schönen Gruß,

brb

//edit: Ausnahme wäre natürlich, wenn alle Zeilen gleich lang sind: Dann könnte man auch mit seek() an die entsprechende Stelle springen - das wäre vermutlich schneller als zu iterieren. Aber gleiche Zeilenlänge ist wahrscheinlich nicht gegeben :)
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

So wuerde ich's auch machen, allerdings wuerde ich enumerate versenden.

Code: Alles auswählen

>>> f = open(".emacs")
>>> for i, line in enumerate(f): print i, line
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

An sich könnte man den Input mit .split("\n\t") in "Tokens" aufteilen und dann
auf Token 500.000 zugreifen. Wäre wenigstens der lineare Aufwand
behoben. Macht natürlich nur Sinn, wenn man auf mehrere Zeilen zugreifen muss,
ansonsten wäre das lineare durchlaufen schneller wenn man nur auf wenige Zeilen
zugreifen will, da du zum Token Erstellen natürlich auch durch musst und wahrscheinlich
für jeden token noch ein neues "String Objekt" erstellt werden muss.

Wie mein Vorredner schon sagte wäre bei fester Blocklänge der Zugriff über einen
offset ideal, aber das wird wohl nicht der Fall sein?

An sich lösst man aber damit nicht den extrem hohen Speicher und IO Aufwand.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

rads hat geschrieben:An sich könnte man den Input mit .split("\n\t") in "Tokens" aufteilen und dann
auf Token 500.000 zugreifen.
Dann haettest du aber wieder die komplette Datei und die komplette Liste im Speicher! (Mal davon abgesehen, dass es dafuer die Methode readlines gibt.)
An sich lösst man aber damit nicht den extrem hohen Speicher und IO Aufwand.
Doch, wenn du zeilenweise ueber die Datei iterierst, umgehst du zumindest das Speicherproblem, da hier nicht die komplette Datei plus eine Liste im Speicher gehalten werden.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Rebecca hat geschrieben:Dann haettest du aber wieder die komplette Datei und die komplette Liste im Speicher! (Mal davon abgesehen, dass es dafuer die Methode readlines gibt.)
Stimmt, ich habe nichts gegenteiliges behauptet. -> immer noch memory error da va überschritten
[edit]
Readline ermöglicht dir nicht das mehrmalige anspringen von einzelnen Zeilen (was ich meinte), bzw. du
müsstest es ebenso zwischencachen, was aufs gleiche hinausläuft (ist vielleicht nicht pythonisch, das mag sein)
[/edit]
rads hat geschrieben:Doch, wenn du zeilenweise ueber die Datei iterierst, umgehst du zumindest das Speicherproblem, da hier nicht die komplette Datei plus eine Liste im Speicher gehalten werden.
Natürlich funktioniert es.Ich wollte mit Aufwand ausdrücken, dass trotzdem 500.000 durch den Windows Cache Manager müssen und dann im Speicher gelangen. Sie müssen nicht gleichzeitig drinnen sein, aber bei der Anzahl der page faults könnt dir "übel" werden.

Naja, an sich funktioniert es. Aber wie mein Prof aus SoftEng so gerne sagte, "funktionieren bedeutet noch lange nicht
bestanden".

Schön ist was anderes wie eigentlich alle erkannt haben, warum nicht auf segmentierte Datein oder direkt auf eine Datenbank zugriffen wird bleibt für mich wohl ein Rätsel.

ich bin ja schon ruhig.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Falls Du die Änderungen sequenziell abarbeiten kannst, reicht zeilenweises Iterieren UND Rausschreiben:

Code: Alles auswählen

with open('infile', 'r') as f_in:
    with open('outfile', 'w') as f_out:
        for i, l in enumerate(f_in):
            f_out.write(do_magic_with_content_at_line_i(i, l))
Für wahlfreien Zugriff wirds schwieriger, da sind andere Datenhalden (DB) oder fixe Blockgrößen besser geeignet. Kommt beides nicht in Frage, könntest Du vorher auch einen Seek-Index der Zeilennummern erstellen und dann mit seek() gezielt bestimmte Zeilen erreichen. Die Änderungen könntest Du temporär vorhalten und nach getaner Arbeit in die Zieldatei schreiben.
BlackJack

@rads: Zu Deinem [edit]: Dir ist bewusst, dass Rebecca in dem Zitat davor von `readlines()` und nicht von `readline()` sprach?
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

BlackJack hat geschrieben:@rads: Zu Deinem [edit]: Dir ist bewusst, dass Rebecca in dem Zitat davor von `readlines()` und nicht von `readline()` sprach?
Oh, dann nehm ich alles zurück, das s hab ich nicht gesehen.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Wenn man häufig auf Zeilen einer (sich nicht ändernden) Datei nicht-linear zugreifen will, kann man auch einen Rücksprung in die 70er Jahre (dort hätten dann aber wohl 3 Bytes für den Index gereicht) wagen und sich schnell selbst eine ISAM-Datei erschaffen:

Code: Alles auswählen

    import struct
    
    def create_index(name):
        with open(name, "r") as lines, open(name + ".idx", "wb") as index:
            while True:
                index.write(struct.pack("<q", lines.tell()))
                if not lines.readline(): break
                
    def get_line(name, i):
        with open(name, "r") as lines, open(name + ".idx", "rb") as index:
            index.seek(i * 8)
            lines.seek(struct.unpack("<q", index.read(8))[0])
            return lines.readline()
            
    create_index("/tmp/test")
    for i in range(2, 5):
        print(i, get_line("/tmp/test", i), end="")
Stefan
crea
User
Beiträge: 5
Registriert: Dienstag 26. März 2013, 21:30

Hallo,

ich versuche mich seit nicht allzu langer Zeit an Python-Programmierung und habe ein ähnliches Problem wie oben beschrieben. Ich möchte Video Processing mit Python realisieren, d.h. ein .avi lesen (das tue ich mittels openCV) und die Frames in ein Numpy-Array übergeben, um damit dann weitere Datenanalyse zu betreiben und Plots zu erstellen.
Wie gesagt tritt bei mir der gleiche MemoryError auf, beim Erstellen der Liste.

Ich würde gern mehr erfahren über die o.g. Möglichkeit, das Problem mit "Streams in Form von Generatoren" zu lösen. Kann mir jemand mehr dazu sagen?
Wär prima, wenn ihr mir weiterhelfen könntet. :wink:
Antworten