Array speichern - Geschwindigkeitsproblem mit f.write()

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
SVPAM
User
Beiträge: 6
Registriert: Mittwoch 18. April 2012, 12:31

Hallo Community,

!!! Ich möchte einen Array so schnell wie möglich speichern !!!

Hierzu habe ich folgenden einfachen Quellcode:

Code: Alles auswählen

 
a=zeros(10000000)                  # Initialisierung des Array a
Time= ExecTime()                   # "Time"-Objekt erzeugen
Time.Start_T= time()               # Start der Zeitmessung
f= open("traditional.dat","w")     # Datei öffnen
for i in range(len(a)):            # For-Schleife zum schreiben des Arrays a in eine Datei
    f.write(str(i)+"\n") 
f.close()                          # Datei schließen
Time.End_T= time()                 # Ende der Zeitmessung
Dauert auf meinem Intel core i5 Prozessor: 29 Sekunden.
(Ich habe den Quellcode, der die Dauer berechnet, der Einfachheit halber weggelassen)

Meine Frage an Euch:
Wie geht es schneller?


Ich habe bereits writeline() und das Pickle Modul ausprobiert, jedoch waren beide langsamer. Ihr habt bestimmt etwas, das viel schneller geht, oder?

Danke und Gruß
SVPAM
Trichter
User
Beiträge: 45
Registriert: Montag 20. April 2009, 10:21

Korrigiert mich, wenn ich falsch liege, aber ist da der entscheidende Faktor nicht die Schreibgeschwindigkeit der Festplatte?
Wesentlich beschleunigen kann man das mit reiner Code-Optimierung nicht mehr.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Abgesehen davon sollte man Dateien so öffnen:

Code: Alles auswählen

with open(...) as filehandler:
    # filehandler ist hier file-object
Damit wird die Datei immer geschlossen (also auch im Fehlerfall).

Dir ist klar, dass Du nicht den Inhalt des Arrays in eine Datei schreibst, sondern einen Index? Den sollte man auch eher mittels `enumerate` erzeugen.

Strings würde ich auch nicht mittels `+` zusammensetzen, sondern `"".format()` benutzen.

Das wichtigste hat Trichter natürlich schon erwähnt. Der Sinn dieser Optimierung erschließt sich mir nicht.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Code: Alles auswählen

>>> a = ["0"] * 10000000
>>> with open("test.txt", "w") as f:
...     f.write("\n".join(a))
... 
>>> 
Dauerte eine oder zwei Sekunden. Oder mache ich was falsch?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Trichter hat geschrieben:Korrigiert mich, wenn ich falsch liege, aber ist da der entscheidende Faktor nicht die Schreibgeschwindigkeit der Festplatte?
Wesentlich beschleunigen kann man das mit reiner Code-Optimierung nicht mehr.
Genau genommen sind es wohl eher die *ständigen* Schreibvorgänge. Als Alternative kann man den zu schreibenden Inhalt auch in einem Rutsch mittels `f.write('\n'.join(mein_array))` in die Datei schreiben. Einziges Manko, was aber erst bei sehr großen Arrays sichtbar wird: Auf diese Weise wird *alles* in den RAM gespeichert. Wenn wir jetzt von nem Gigabyte an Gesamtinhalt sprechen oder so, dann muss man sich etwas ausdenken, was die Sache häppchenweise erledigt. Zunächst würd ich's aber mal mit der vorgeschlagenen Lösung ausprobieren.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hyperion hat geschrieben:Dir ist klar, dass Du nicht den Inhalt des Arrays in eine Datei schreibst, sondern einen Index? Den sollte man auch eher mittels `enumerate` erzeugen.
Falls man wirklich nur den Index will (was ich allerdings auch nicht so recht glaube), dann ist `range(len(iterable))` IMHO schon ganz ok. Unter Python 2.x natürlich optimalerweise `xrange()` statt `range()`.
SVPAM
User
Beiträge: 6
Registriert: Mittwoch 18. April 2012, 12:31

Hallo Forumsmitglieder,

BÄRENSTARK! Der Array wurde in 1 Sekunde gespeichert mit dem Befehl:

Code: Alles auswählen

f.write("\n".join(a))
Vielen Dank, das ist eine Wucht!

Zu Euren Fragen:
...ist da der entscheidende Faktor nicht die Schreibgeschwindigkeit der Festplatte? Wesentlich beschleunigen kann man das mit reiner Code-Optimierung nicht mehr.
Na ja, wie webspider eindrucksvoll gezeigt hat, kann man die Dauer doch auf ca. < 1 Sekunde reduzieren (jedenfalls habe ich die Festplatte nicht getauscht :o).
`f.write('\n'.join(mein_array))` ... Auf diese Weise wird *alles* in den RAM gespeichert. ... muss man sich etwas ausdenken, was die Sache häppchenweise erledigt.
Sehr guter Einwand snafu! Das ist bei mir bereits eingetreten. Mit dieser Technik beschäftige ich mich gerade, weiß jedoch nicht genau, woher ich - bei veränderlichem Arrayinhalt - weiß, wie groß die Häppchen denn maximal sein dürfen?
Dir ist klar, dass Du nicht den Inhalt des Arrays in eine Datei schreibst, sondern einen Index?
Ähhh ... verstehe ich nicht ganz. In meinem Programm enthält das Array natürlich irgendwelche float-Werte, aber für meine Frage wollte ich es aufs Wesentliche reduzieren und der Einfachheit halber einen Array mit lauter Nullen vorstellen. Habe ich da etwas übersehen?

Vielen Dank für Eure tollen Hilfen!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

SVPAM hat geschrieben: In meinem Programm enthält das Array natürlich irgendwelche float-Werte, aber für meine Frage wollte ich es aufs Wesentliche reduzieren und der Einfachheit halber einen Array mit lauter Nullen vorstellen. Habe ich da etwas übersehen?
Nö, das ist ja noch alles korrekt. Aber Du schreibst ja nicht die Nullen in die Datei, sondern Integer-Werte mit dem Index, also "1, 2, 3, 4, ...". Darauf wollte ich nur hinweisen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Da du mit numpy zu arbeiten scheinst: ``numpy.save`` ist dir bekannt?
Das Leben ist wie ein Tennisball.
SVPAM
User
Beiträge: 6
Registriert: Mittwoch 18. April 2012, 12:31

Aber Du schreibst ja nicht die Nullen in die Datei, sondern Integer-Werte mit dem Index, also "1, 2, 3, 4, ...".
Ahhh ... jetzt! Natürlich, habe ich übersehen. Du hast natürlich Recht :)
``numpy.save`` ist dir bekannt?
Ja, das habe ich gleich anfangs ausprobiert, jedoch keinen Geschwindigkeitsgewinn erzielen können.

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

SVPAM hat geschrieben:Sehr guter Einwand snafu! Das ist bei mir bereits eingetreten. Mit dieser Technik beschäftige ich mich gerade, weiß jedoch nicht genau, woher ich - bei veränderlichem Arrayinhalt - weiß, wie groß die Häppchen denn maximal sein dürfen?
Naja, du kannst dir ja einfach immer 10000 Elemente (Floats, sagtest du ja) schnappen und diese dann in die Datei schreiben. Da sollte jeder halbwegs zeitgemäß ausgestattete Rechner RAM-technisch locker mit klarkommen. Ggf durch Ausprobieren schauen, wo der optimale Kompromiss zwischen Speicherverbrauch und `write()`-Zugriffen liegt. Wobei du da natürlich beachten musst, ob das Programm nur auf deiner Kiste läuft oder auch noch anderswo (wo entsprechend andere technische Gegebenheiten herrschen) eingesetzt wird. Ich finde die besagten 10000 nicht verkehrt. Evtl möchtest du in deiner Schreibmethode einfach einen Parameter mit Standardwert anlegen, den der Benutzer bei Bedarf verändern kann.
SVPAM
User
Beiträge: 6
Registriert: Mittwoch 18. April 2012, 12:31

Naja, du kannst dir ja einfach 10000 Elemente (Floats, sagtest du ja) schnappen und diese dann in die Datei schreiben.
Ja, so werde ich es machen. Hatte mit meinen Kollegen hier zwar an 10^5 gedacht, aber das ist ja dann ausprobierbar, wie Du selber schreibst.

Danke!
Ich denke, das Thema ist damit beantwortet.
Eure "post-Geschwindigkeit" ist beeindruckend :)

Gruß
SVPAM
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

SVPAM hat geschrieben:Ja, das habe ich gleich anfangs ausprobiert, jedoch keinen Geschwindigkeitsgewinn erzielen können.
Ich kann damit locker 10 Mio. Elemente speichern, ohne dass ich überhaupt merke, dass es eine Verzögerung gibt.
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich hab jetzt auch etwas doof gerechnet (Größenverhältnisse nicht wirklich beachtet). 10 Mio sind wahrscheinlich wirklich sinnvoller... ^^
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Vielleicht interessiert dich auch sys.getsizeof(), welches den Speicherverbrauch für ein Python-Objekt angibt. Laut Doku funktioniert das mindestens für Pythons Builtin-Typen zuverlässig. Bei mir belegt ein leerer Python-String 37 Bytes. Jedes Zeichen im String belegt ein weiteres Byte. Könnte zur Orientierung ganz nützlich sein.

//edit: Dann aber wohl eher bezogen auf das Array. Beim String kann man ja einfach die durchschnittliche Länge der Floats nehmen. Grundsätzlich ist das aber viel Hokuspokus, der wahrscheinlich nicht nötig sein wird. Stumpfes Ausprobieren ist hier sicher der bessere Weg... :)
SVPAM
User
Beiträge: 6
Registriert: Mittwoch 18. April 2012, 12:31

``numpy.save`` ist dir bekannt? ... Ich kann damit locker 10 Mio. Elemente speichern
Habe es gerade ausprobiert und es tut wirklich in <1sec:

Code: Alles auswählen

b=numpy.array(1e9)
outfile = 'numpysave.dat'
numpy.save(outfile, b)
Muss mir noch anschauen, was es mit diesem .npy Format auf sich hat und wie ich das für die nachgelagerte Auswertung nutzen kann. Habe schon http://docs.scipy.org/doc/numpy/referen ... .save.html gefunden.

Für alle, die so etwas auch interessant finden:

Code: Alles auswählen

a = ["0"] * 1000000000 # also 9 Zehnerpotenzen
führt zum Fehler:
"a = ["0"] * 1000000000
Memory Error"
wohingegen

Code: Alles auswählen

b=numpy.array(1e9)
problemlos angenommen und in <1sec verarbeitet wird.
BlackJack

@SVPAM: Dir ist aber schon klar, dass ``numpy.array(1e9)`` kein Array erzeugt, sondern *einen* Skalarwert!?

Code: Alles auswählen

In [248]: a = np.array(1e9)

In [249]: a
Out[249]: array(1000000000.0)

In [250]: len(a)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/bj/<ipython console> in <module>()

TypeError: len() of unsized object

In [251]: a.ndim
Out[251]: 0
SVPAM
User
Beiträge: 6
Registriert: Mittwoch 18. April 2012, 12:31

dass ``numpy.array(1e9)`` kein Array erzeugt, sondern *einen* Skalarwert!?
Ok ... wieder etwas dazugelernt.
Antworten