Große Listen erzeugen

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
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Hi Community,

ich bin gerade dabei ein paar stress tests mit meinem neuen computer durchzuführen, dabei will ich große listen erzeugen und an denen arbeiten,
leider scheitet es bereits an der erstellung der list:

Code: Alles auswählen

xrange(20000000000000000000)
gibt mir ein
(OverflowError: long int too large to convert to int)
Mir ist klar, dass die Funktion xrange hier ein int erwartet

Dann hab ich mir gedacht, schreib ich mir eine ähnliche Funktion

Code: Alles auswählen

length = 200000000
data = []
counter = 0
while(True):
    if counter == length:
        break
    data.append(counter)
    counter += 1
Da bekomme ich aber bei etwa 2 GB im Hauptspeicher einen MemoryError bei data.append(counter)

Habt ihr irgendwie Wege wie ihr große Listen erstellt?

Ich benutzte python 32 bit 2.6 - 4 GB RAM
Dackel
User
Beiträge: 11
Registriert: Mittwoch 4. März 2009, 20:44

Hallo anogayales,

bist Du Windowsnutzer?

In 32-bit-Systemen können einzelnen Programmen jeweils nur insg. 4GB Speicher zur Verfügung gestellt werden. Hinzukommt, dass der Windows-Kernel 2GB davon für sich beansprucht. Bleiben 2GB Adressraum für das Programm übrig und dementsprechend fliegt dir dein Pythonprogramm bei diesem Wert um die Ohren.

Lösungen:
- 64-bit-System samt 64-bit-Python aufsetzen
- Es gibt AFAIK noch ein Compiler-Flag, mit dem der Kernel gezwungen werden kann, nur 1GB Adressraum zu beanspruchen, dafür muss aber die Software entsprechend kompiliert sein. Da ich nicht mit Windows arbeite, weiß ich nicht ob das bei den Binaries von python.org der Fall ist.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Ja, ich bin Windowsnutzer,

wie kommt es aber, dass eine Liste von 20 000 000 Integern, 2 GB Speicher fressen?

20 Millionen * 4 Byte = 78 125 KByte

Und der Overhead um die zu erstellen kann doch auch keine Gb maße annehmen?

Das schockiert mich doch schon ein wenig.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dackel hat geschrieben:- Es gibt AFAIK noch ein Compiler-Flag, mit dem der Kernel gezwungen werden kann, nur 1GB Adressraum zu beanspruchen, dafür muss aber die Software entsprechend kompiliert sein.
Ich meine eher dass das ein Kernel-Flag war, was zum Booten angegeben wird. Als Compilerflag macht das IMHO wenig Sinn.
anogayales hat geschrieben:wie kommt es aber, dass eine Liste von 20 000 000 Integern, 2 GB Speicher fressen?

20 Millionen * 4 Byte = 78 125 KByte

Und der Overhead um die zu erstellen kann doch auch keine Gb maße annehmen?
Wo hast du denn die 4 Byte her? 4 Byte sind ja nur C-Integer auf 32-Bit-Architekturen wie x86. Aber wer sagt denn, dass Python-Integer direkt den C-Integern entsprechen?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Höchstwahrscheinlich sind es ja dann keine C-Integer. Aber der Speicherverschnitt muss doch um einiges größer sein für einen Integer. Ich frag mich nur welchen Sinn es macht, eine Integer Implementierung so aufzublasen?

In Python sind ja Integer auch Objekte, gibt es dein ein Packet, dass 32 bit integer ohne großen Verschnitt bereitstellen kann?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

anogayales hat geschrieben:Höchstwahrscheinlich sind es ja dann keine C-Integer. Aber der Speicherverschnitt muss doch um einiges größer sein für einen Integer. Ich frag mich nur welchen Sinn es macht, eine Integer Implementierung so aufzublasen?
Naja, Python Integer sind Objekte, das macht die Handhabung wesenlich angenehmer als in etwa Java, wo ``int`` ein primitiver Typ ist und daher einer Reihe von Einschränkungen unterworfen ist.
anogayales hat geschrieben:In Python sind ja Integer auch Objekte, gibt es dein ein Packet, dass 32 bit integer ohne großen Verschnitt bereitstellen kann?
Ich schätze mal numpy/scipy machen sowas, ctypes arbeitet teilweise auch mit C-Integern.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
...
User
Beiträge: 116
Registriert: Mittwoch 23. Dezember 2009, 20:22

Gibt es bei Python nicht auch sowas wie 32-bit Integer?
Mir scheint es, es sei ein signed Integer.
Da haben wir dann einen Wertebereich von -2.147.483.647 - +2.147.483.647
mit deinen 20.000.000.000.000.000.000 bist du da um das vielfache höher.

Damit fällt 20000000000000000000 unter long.
(

Code: Alles auswählen

>>>type(20000000000000000000)
<type 'long'>
>>>type(int(20000000000000000000))
<type 'long'>
>>>
)

xrange benötigt aber int, und wirft darum, weil die Konvertierung nicht funktioniert den Error...

Wäre meine Erklärung.

Was für einen Speicherbedarf 'long' hat, weiß ich nicht.


lg,
...
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

... hat geschrieben:xrange benötigt aber int, und wirft darum, weil die Konvertierung nicht funktioniert den Error...

Wäre meine Erklärung.
Sehr schön, allerdings hat das der Fragesteller bereits erklärt und das wurde auch nicht in Zweifel gezogen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

... hat geschrieben:Gibt es bei Python nicht auch sowas wie 32-bit Integer?
Naja, ein Integer in Python kann so groß werden wie (signed) Integer auf der jeweiligen Architektur groß sind. Bei 32-Bit Systemen ist der maximale Wert 2147483647 (2**31-1) und bei 64-Bit Systemen ist das 9223372036854775807 (2**63-1).

Jedoch kann man in Python in aller Ruhe auch größere Zahlen verwenden, denn Integer werden automatisch auf longs vergrößert und die haben kein Größenlimit.
... hat geschrieben:Was für einen Speicherbedarf 'long' hat, weiß ich nicht.
Die sind beliebig groß. Kann auch den ganzen virtuellen Speicher ausfüllen, so ein long.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

anogayales hat geschrieben:20 Millionen * 4 Byte = 78 125 KByte
20 Millionen vielleicht schon, allerdings sind es in deinem gepasteten Code keine 20 Millionen. Wenn man dann noch bedenkt, dass ein Long etwa 14 Byte belegt und als Element in der Liste mindestens noch eine weitere Zeigerlänge benötigt, kommt da doch etwas zusammen.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
BlackJack

@anogayales: Es gäbe noch das `array`-Modul in der Standardbibliothek.

Integer so "aufzublasen" hat den Sinn, dass sie einfach wie jedes andere Objekt auch implementiert werden können. Sonst müsste man die in der Implementierung an jeder Stelle gesondert handhaben. Ein Methode bei VMs ist zum Beispiel 31-Bit Integer in den Pointer zu packen, der sonst auf Objekte zeigt und zwar um ein Bit nach links verschoben und mit dem untersten Bit auf 1 gesetzt. Das wäre dann eine ungerade Zahl. Da Daten auf modernen Systemen in der Regel mindestens auf durch Zwei teilbare Adressen ausgerichtet sind, kann man so an diesem Bit eine Zahl von einem anderen Objekt unterscheiden. Der Preis: die Implementierung muss bei jedem Objektzugriff prüfen ob's ein echtes Objekt oder eine Zahl ist.

Die Frage ist also: Speicher "verschwenden" und auf die Nase fallen wenn jemand *wirklich* *viele* Zahlen anlegen will, oder bei *jedem* Programm Geschwindigkeit einbüssen, nur für den Fall, das mal jemand auf die Idee kommt viele Zahlen haben zu wollen.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Also mit numpy kann ich glücklicherweise int32 Integer erzeugen, die nicht meinen verfügbaren Speicherplatz auffressen. :wink:

Bei 20 Millionen brauch ich ungefähr 500 MB Speicherplatz
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hast du mal ein

Code: Alles auswählen

[0]*20000000
versucht? Mit dem schrittweisen append zerlegst du dir vielleicht einfach nur ganz schnell den Speicher.
Das Leben ist wie ein Tennisball.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

anogayales hat geschrieben:Also mit numpy kann ich glücklicherweise int32 Integer erzeugen, die nicht meinen verfügbaren Speicherplatz auffressen. :wink:

Bei 20 Millionen brauch ich ungefähr 500 MB Speicherplatz
Das liegt wohl an 64bit. ;) In 32bit braucht ein Array mit 20Mio Python-Integern nur 250MB Speicher. Gegenüber den 80 MB die man in einem reinen Array brauchen würde, ist das gar nicht so viel Overhead, viel kleiner kriegt man das schwer.

Zu deinen 2GB: Du hast dich in deinem Script verschrieben, da stehen 200 Mio, damit kommt man durchaus auf 2GB.

Aber das wurde ja alles schon gesagt…
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

In Python 2.6.2 ist ein "int" so definiert:

Code: Alles auswählen

typedef struct {
    Py_ssize_t ob_refcnt;           
    struct _typeobject *ob_type;
    long ob_ival;
} PyIntObject;
Das sind mindestens 16 Bytes bei 32-Bit-Compilern und 24 bei 64-Bit-Compilern. Kleine Zahlen um 0 herum sind vorinitialisiert und werden wiederverwendet. Der Rest wird jedes Mal angelegt.

Der klassische, wenn auch nicht ANSI-C-Standardkonforme Weg wäre, kleine Datentypen wie "int", "bool" oder "char" als "tagged immediate values" wie Pointer zu verwalten. Das wäre effizienter. Dafür hat dann ein "int" auch nur 30 oder 31 Bits. Entweder nutzt man als, dass Zeiger immer auf Vielfache von 4 oder 8 zeigen und somit ihre untersten 2 oder 3 Bits immer 0 sind und man daher Zahlen, dadurch markieren kann, dass ihr unterstes Bit immer gesetzt ist oder man dreht die Kodierung um und markiert Zeiger durch ein gesetztes Bit und sorgt dafür, das Integer immer eine 0 im untersten Bit haben. Je nach Prozessor ist mal das eine und mal das andere Verfahren besser. Auf einer SPARC-Architektur markiert man IIRC besser Zeiger, weil es egal ist, was da steht, denn das wird vom Prozessor ignoriert. Ein 68000er wiederum reagiert allergisch, wenn die Adressregister eine ungerade Adresse enthalten und da ist war es besser, Zahlen zu markieren. Wie das auf den heute allgegenwärtigen Intel-Maschinen ist, weiß ich nicht.

In einem 64-Bit-System könnte man aber damit experimentieren, das oberste Byte als Tag zu benutzen. So große Adressräume gibt es eh nicht und Zahlen könnten dann immer noch 56-Bit haben, bevor man auf eine andere Darstellung wechseln muss. Vielleicht könnte man sogar direkt einen Index auf die Klasse als Tag ablegen, für den man sich dann 16 Bits genehmigt.

Fazit: Die Speicherverwaltung von CPython ist ineffizient und verschwenderisch. Und Referenzen zu zählen macht das System langsamer als hätte man nur einen modernen (d.h. Stand von 1986) Garbage Collector.

Doch ich schweife ab...

Stefan
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Darii hat geschrieben:
anogayales hat geschrieben:Also mit numpy kann ich glücklicherweise int32 Integer erzeugen, die nicht meinen verfügbaren Speicherplatz auffressen. :wink:

Bei 20 Millionen brauch ich ungefähr 500 MB Speicherplatz
Das liegt wohl an 64bit. ;) In 32bit braucht ein Array mit 20Mio Python-Integern nur 250MB Speicher.
Bei mir (auch 64 bit) sind es "nur" ca. 150 MiB mit numpy bei arange(int(20e6)) (das int(), weil ich mich bei großen Zahlen so leicht vertippe und 20e6 da weniger anfällig ist ;-) ). Also, genau zu messen was, wann, wofür von Python in Beschlag genommen wird ist nicht ganz so leicht. Aber der Unterschied der Schätzungen von 150 MiB oder 250MB zu 500 MB erscheint mir doch ein wenig zu groß, um realistisch zu sein. Mindestens eine Zahl ist da nicht stimmig.

Gruß,
Christian
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wie bestimmt ihr denn überhaupt den Speicherverbrauch?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Ich nenne es lieber "Schätzen" und weiß, dass die Methode, na ja, ungenau ist: Python starten, Taskmanager des OS (oder top) auslesen, (in diesem Fall numpy importieren, wieder auslesen), Objekt anlegen, auslesen, Differenz bilden, fertig.
Antworten