Hallo zusammen,
Ich schreibe derzeit an einem Programm, das mit sehr großen Textdateien
umgehen muss (bis zu 10 Millionen Zeilen).
Das merkwürdige dabei ist, dass das Reservieren des Speichers für einen
String entsprechender Größe (z.B. 20 MB, entspricht ca. 1 Mio. Zeilen)
länger dauert als das Parsen des Strings und die anschließende
Generierung eines zweiten Strings (kürzer) aus den entsprechenden Daten.
Nun ist ja Visual Basic 5 nicht gerade für seine tolle Geschwindigkeit
bekannt, aber auch mit der API-Variante HeapAlloc geht es nicht wirklich
schneller als die Standardversion "Space$(AnzahlderBytes)".
Außerdem musste ich feststellen, dass die Dauer für das Reservieren
keineswegs linear mit der Speichergröße ansteigt, wie ich jetzt naiv
vermutet hätte:
1
100.000 Zeilen - 0,13s
2
1.000.000 Zeilen - 16s
3
10.000.000 Zeilen - läuft schon etwas länger, reiche ich ggf. nach
Ist das normal? (der Rechner hat 16GB RAM, sollte also nicht knapp
werden)
update: Die 10 Mio. Zeilen-Datei (188MB) ist gerade eben mit einem
Speicherfehler abgebrochen. Alleine das Reservieren des Speichers hätte
damit also mindestens gute 50min gebraucht, wenn es jetzt fertig gewesen
wäre - und das auf einem Quadcore.
Im Taskmanager konnte ich sehen, dass das Programm sich langsam an 1GB
(?) Ramverbrauch herangetastet hat, was meiner Meinung nach viel zu viel
ist.
Eine kurze Testausgabe hat aber bestätigt, dass die Anzahl an Zeichen
für das Reservieren mit "Space$()" korrekt ermittelt wird.
@ vn nn (wefwef_s):
Selbstverständlich könnte man das auch Häppchenweise aus der Datei
lesen, aber ich habe ja eigentlich genug RAM frei, und Operationen im
RAM sollten normalerweise doch schneller sein, oder?
Irgendwie kommt es mir seltsam vor, dass das Anlegen von so einem
bisschen Speicher in einer Variablen so langsam sein kann. (insbesondere
bei den 188MB/10Mio. Zeilen)
Was verstehst du unter einer Zeile? Einen dynamisch verwalteten String?
Dass das Anlegen von 10M String-Handlern + 10M dynamischen
String-Blöcken langsam ist ist dann klar. Was hälst du davon, einfach
einen einzelnen 188 MByte großen String anzulegen und die Datei dort
reinzulesen? Du könntest dann zusätzlich ein Array mit 10M Integern
anlegen und dort die Indices der Zeilenanfänge speichern, falls du
schnell bestimmte Zeilen finden musst.
Die Tatsache, dass du von Zeilen sprichst, legt die Vermutung nahe dass
es sich um ein textbasiertes Format handelt. Wäre es nicht sinnvoller
die Daten beim einlesen direkt in eine Art Binärformat/Datenstrukturen
umzuwandeln und so effizienter im RAM zu speichern?
LangsamerSpeicher? schrieb:> ... (bis zu 10 Millionen Zeilen).>> ... ja Visual Basic 5
Hallo,
die beiden Sachen würden mir zusammen schon mal Bauchschmerzen bereiten.
Erläutere doch mal deine Beweggründe warum du das so machen willst, wie
du es jetzt versuchst. Ist das wirklich die beste Lösung?
Schau dir mal Themen wie "Data Mining" und "BigData" an.
Also erklär mal was du wirklich willst. Und ob du vielleicht auch
(sorry) mit richtigen Programmiersprachen kannst...
Grüße aus Berlin
Der Speicher wird falsch alloziert. Wenn so ein Vorgang zu lange dauert,
wird der String incrementell erhoeht. Dh Specher allozieren, rein mit
dem String, dann ein Stueck Speicher wie bisher plus das Increment, eine
Zeile, allozieren, alles kopieren und so weiter.
Das muss man anders anpacken. Schauen, wie gross die Datei ist, inkl.
steuerzeichen. Dann dieses Stueck allozieren, auch wenn's 500MByte ist.
Und dann alles am Stueck rein, in einem Binaertransfer.
LangsamerSpeicher? schrieb:> Nun ist ja Visual Basic 5 nicht gerade für seine tolle Geschwindigkeit> bekannt, aber auch mit der API-Variante HeapAlloc geht es nicht wirklich> schneller als die Standardversion "Space$(AnzahlderBytes)".
Hast du das getestet, indem du in einem Testprogrogramm nur HeapAlloc
bzw. Space$ aufgerufen hast? Das kann unmöglich so lange dauern. Ich
kein Windows und erst recht kein VB, hab's aber aus Neuigier mal unter
Linux in C mit malloc auf meiner alten P4-Gurke getestet:
10.000.000 Blöcke mit je 20 Bytes (insgesamt 2E8 Bytes): 1,045 s
1 Block mit 200.000.000 Bytes (insgesamt 2E8 Bytes): 0,865 s
Dabei habe ich den kompletten reservierten mit fortlaufenden Zahlen
füllen lassen und ein Byte davon wieder ausgelesen, um sicher zu gehen,
dass der Speicher nicht nur virtuell reserviert wird.
Windows ist zwar schlecht, aber sicher nicht so schlecht, dass es für
die gleiche Aktion auf einem deutlich moderneren Prozessor 50 Minuten
und mehr braucht.
Ich glaube eher, ein anderer Programmteil braucht viel Rechenzeit, und
du führst dies irrtümlicherweise auf die Speicherreservierung zurück.
Yalu X. schrieb:> ich glaube eher, ein anderer Programmteil braucht viel Rechenzeit, und> du führst dies irrtümlicherweise auf die Speicherreservierung zurück.
Wenn er sowas macht:
str = str + neue_zeilen
wird im blödestem Falle folgendes ausgeführt:
- Allozieren str.len + neue_zeilen.len bytes
- Erzeuge Verwaltungsinfos für String
- Kopieren str + neue_zeilen
- gebe alten Speicher von str frei
Wenn er das 10 Millionen(!!!) mal macht muss er sich nicht wundern wenn
das elendig lange dauert.
Ein einmaliges malloc ist natürlich extrem schnell im Vergleich.
>Was hälst du davon, einfach einen einzelnen 188 MByte großen String anzulegen und>die Datei dort reinzulesen?
Genau so habe ich es gemacht.
>Hast du das getestet, indem du in einem Testprogrogramm nur HeapAlloc>bzw. Space$ aufgerufen hast?
Die Zeitmessung lief nur über den "Space$()" bzw "HeapAlloc()"-Befehl
und das Programm hat auch nichts nebenbei zu tun gehabt.
Alleine das Anlegen der Variablen hat so lange gedauert, warum auch
immer...
Ich habe es heute noch mal mit der "Space$()"-Funktion in Visual Studio
2012 ausprobiert und da lief alles problemlos und in einer eher zu
erwartenden Geschwindigkeit (200MB deutlich unter einer Sekunde) ->
Jetzt habe ich endlich mal einen Grund, mich ins neuere Visual Studio
einzuarbeiten ;-)
Was bedeutet "umgehen"? Lesen oder auch ändern?
Für so was bieten moderne OSe "Memory Mapped Files". Die werden in den
Adressraum eingeblendet, du hast ja mit 16GB RAM sicherlich 64Bit und
damit satt genug davon, und können wie normalen Speicher behandelt
werden, d.h. keine IO Aufrufe, nur das dieser quasi mit dem Inhalt der
Datei initialisiert ist. Wurde R/W geöffnet, dann kann man auch ändern,
was wiederum auf der Datei landet. Vergrössern/verkleiner geht
prinzipiell auch, ist aber etwas aufwendiger in der Handhabung.
OS-übergreifend macht das Java mit NIO.
VB kann das im Prinzip auch, da man ja DLL-Routinen aufrufen kann, aber
wenn man MemMappedIO in VB hinbekommt, dann weiß man auch wie das in
C[++|#]? geht.
Ergänzung: die Datei nimmt dann am Paging teil, d.h nur was benutz wird
muß im Speicher stehen, der unbenutzte Rest bleibt auf Platte. Genau so
werden BTW auch Programm/DLL's/sharedLib's behandelt. Das sind einfach
R/O MemMapped Files.
@Profis: ich weiß manche Teile davon sind CopyOnWrite, d.h. werden mit
den Daten des Files initialisiert und können geändert werden, ohne das
sich das auf das File auswirkt. Aber erst mal R/O, zum Einstieg ;-)
LangsamerSpeicher? schrieb:> Nun ist ja Visual Basic 5 nicht gerade für seine tolle Geschwindigkeit> bekanntLangsamerSpeicher schrieb:> Jetzt habe ich endlich mal einen Grund, mich ins neuere Visual Studio> einzuarbeiten ;-)
Irgendwie bezweifel ich, das eine IDE so dermaßen das Zeitverhalten
deiner Applikation beeinträchtigt...
Es gibt tolle Komponenten, die sind sehr gut und praktisch fuer kleine
Textfiles. Dan kann man zB die Zeile per array zugreifen. Bei grossen
files koennen solche komponenten eine Katastrophe sein. Ich hatte mal
ein Stringgrid, das konnte sich von einem CSV file laden. 30000 Zeilen
zu laden dauerten eine Viertel Stunde. Sowas muss man dann eben von
Grund auf neu schreiben.