Seite 1 von 1
Große Listen erzeugen
Verfasst: Donnerstag 25. Februar 2010, 14:54
von anogayales
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:
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
Verfasst: Donnerstag 25. Februar 2010, 15:10
von Dackel
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.
Verfasst: Donnerstag 25. Februar 2010, 15:25
von anogayales
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.
Verfasst: Donnerstag 25. Februar 2010, 15:35
von Leonidas
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?
Verfasst: Donnerstag 25. Februar 2010, 15:46
von anogayales
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?
Verfasst: Donnerstag 25. Februar 2010, 15:51
von Leonidas
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.
Verfasst: Donnerstag 25. Februar 2010, 16:27
von ...
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,
...
Verfasst: Donnerstag 25. Februar 2010, 16:35
von /me
... 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.
Verfasst: Donnerstag 25. Februar 2010, 17:06
von Leonidas
... 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.
Verfasst: Donnerstag 25. Februar 2010, 17:44
von Trundle
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.
Verfasst: Donnerstag 25. Februar 2010, 18:47
von 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.
Verfasst: Donnerstag 25. Februar 2010, 21:45
von anogayales
Also mit numpy kann ich glücklicherweise int32 Integer erzeugen, die nicht meinen verfügbaren Speicherplatz auffressen.
Bei 20 Millionen brauch ich ungefähr 500 MB Speicherplatz
Verfasst: Freitag 26. Februar 2010, 00:07
von EyDu
Hast du mal ein
versucht? Mit dem schrittweisen append zerlegst du dir vielleicht einfach nur ganz schnell den Speicher.
Verfasst: Freitag 26. Februar 2010, 09:28
von Darii
anogayales hat geschrieben:Also mit numpy kann ich glücklicherweise int32 Integer erzeugen, die nicht meinen verfügbaren Speicherplatz auffressen.
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…
Verfasst: Samstag 27. Februar 2010, 13:35
von sma
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
Verfasst: Samstag 27. Februar 2010, 15:00
von CM
Darii hat geschrieben:anogayales hat geschrieben:Also mit numpy kann ich glücklicherweise int32 Integer erzeugen, die nicht meinen verfügbaren Speicherplatz auffressen.
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
Verfasst: Sonntag 28. Februar 2010, 08:20
von jens
Wie bestimmt ihr denn überhaupt den Speicherverbrauch?
Verfasst: Sonntag 28. Februar 2010, 14:37
von CM
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.