Laden...

[gelöst] Wie große Files schnell einlesen ohne RAM zu verbrauchen?

Erstellt von Joetempes vor 12 Jahren Letzter Beitrag vor 12 Jahren 4.685 Views
Joetempes Themenstarter:in
888 Beiträge seit 2007
vor 12 Jahren
[gelöst] Wie große Files schnell einlesen ohne RAM zu verbrauchen?

Hallo,

ich möchte mit meinem LogViewer große Files einlesen können.

Und zwar so wie das der TotalCommander (F3) oder der LogViewer Baretail macht.

D.h. egal wie groß das File ist, es ist erstens sofort anschaubar, und zweitens wird auch nichts in den RAM geladen.

Wie machen die das?

Aktuell verfolge ich den Ansatz alle Zeilen einzeln auszulesen, so schaffe ich auch 50.000 Zeile / Sekunde, aber das kommt nicht annähernd an das oben beschriebene Verhalten ran.

Grüße

Joe

1.820 Beiträge seit 2005
vor 12 Jahren

Hallo!

Evtl. werden bei den o.g. Programmen nur die Zeilen-Positionen eingelesen und dann erst bei Scrollvorgängen auf Sichtbarkeit geprüft und eingelesen (einzeln, in 10er-Blöcken, ...).

Nobody is perfect. I'm sad, i'm not nobody 🙁

16.825 Beiträge seit 2008
vor 12 Jahren

Hallo,

die mir schnellste bekannte Art ein File komplett einzulesen ist BufferedStream zu nutzen und die BufferSize der Datei anzupassen.
Das nutze ich um Hashes von meinen Dateien herzustellen und benötige für 3GB-Dateien <0.7 Sekunden ((ist meine derzeit größte, einzelne Datei in der Anwendung).

F
10.010 Beiträge seit 2004
vor 12 Jahren

Was ist denn das für ein Laufwerk, das ca 4GB/Sekunde schafft?

16.825 Beiträge seit 2008
vor 12 Jahren

Verbund aus mehreren Laufwerken, RAID DP.

U
1.688 Beiträge seit 2007
vor 12 Jahren

Wie machen die das?

Vielleicht hiermit: MapViewOfFile
MapViewOfFile function

Edit: .Net4 hat da auch was
Working with memory mapped files in .NET 4

Hinweis von Abt vor 12 Jahren

Bitte klare Linknamen verwenden!

S
401 Beiträge seit 2008
vor 12 Jahren

Hallo,

wie bereits erwähnt musst du nicht die komplette File einlesen. Bei deinen Dateien handelt es sich wohl um Textfiles mit ASCII-Codierung.

Ich persönlich tendiere zu Einteilungen.
Hierzu splitte ich die Datei in Blöcken auf z.B. ein Block ist 1 MB groß und lade immer 3 Blöcke in den Arbeitsspeicher (sofern möglich). Der zweite Block beinhaltet immer den geraden angezeigten Text. Der erste Block den vorhergehenden und der dritte den folgenden Text -> diese Blöcke dienen als Buffer.
So, jetzt müssen wir uns noch Gedanken über das Laden der Datei anstellen. Wir benötigen noch für jeden Block einen Einstiegspunkt in die Datei. Hierzu müssen wir die Datei analysieren. Sagen wir mal, dass der erste Block bei File.movePointerToLocation (0) (Dateianfang) beginnt. Dann verschieben wir den Pointer um 10241024 = 1MB File.movePointerToLocation (0 + 10241024). Jetzt gehen wir solange Rückwärts bis wir das Zeilenumbruchbyte haben und setzen an dieser Stelle den Index für den zweiten Block. Diesen Vorgang wiederholt man jetzt bis zum Dateiende.

Nach dieser Maßnahme kannst du folgendes machen

// lade die Daten für Block 21
block21Beginn = index[20];
block21End     = index[21];
block21 = new Buffer (1024*1024);
// block21 = bufferPool.getFreeBuffer();

// z.B. für Magic-Byte oder Header
offset = 0

file = openFile
file.movePointerTo (block21Beginn + offset)
file.readData (block21, sizeToRead = block21End-block21Beginn)
Hinweis von herbivore vor 12 Jahren

Die folgenden beiden Beiträge wurden von TraceEye - Professional LogViewer hierher verschoben. Bitte keine fachlichen Diskussionen in Projekte-Threads.

U
1.688 Beiträge seit 2007
vor 12 Jahren

Als Erklärung: Es ist nicht wirklich oberhead den ich erzeuge, sondern einfach pro Zeile
ein ListViewItem mit 3 SubItems, die in der ListView gehalten werden.

Vielleicht hilft Dir außer einer Lösung zum Daten lesen auch der VirtualMode

Edit: Tippfehler behoben

Joetempes Themenstarter:in
888 Beiträge seit 2007
vor 12 Jahren

Hallo ujr,

vielen Dank für den Hinweis zum VirtualMode, aber die ListView läuft bereits im VirtualMode. Vielleicht mach ich auch da noch was falsch, ich schau es mir in jedem Fall nochmal an.

Grüße

Joetempes Themenstarter:in
888 Beiträge seit 2007
vor 12 Jahren

@All
Danke für Eure Hilfe, mittlerweile habe ich eine Zwischenlösung implementiert, wobei der RAM Verbrauch drastisch reduziert wurde, der VirtualMode richtig verwendet wird, und ~1MIO Zeile in ~1SEK gelesen werden.

@Abt
Das Probier ich in jedem Fall mal aus.

@Siassei
Ich denke so werd ich es machen.

Joetempes Themenstarter:in
888 Beiträge seit 2007
vor 12 Jahren

Ich hab das Problem jetzt gelöst, hier die wesentlichen Lösungsschritte:

  • File über ReadBlock() in Schleife einlesen
  • Alle Zeichen im Puffer durchiterieren nach "\n"
  • Position von "\n" in List<long> speichern
  • List<long>-Count ist gleich VirtualListSize der ListView im VirtualMode
  • Im retreiveVirtualItem-Ereignis den Eintrag aus der List<long> holen
  • Mit dem Eintrag aus der List<long> an die Position im File springen und die Zeile auslesen

Auf meinem PC mit einer Core2Duo CPU erreiche ich eine Lesegeschwindigkeit von ~ 1MIO Zeilen Pro Sekunde,
d.h. ein ~200 MB File wird in < 3 Sek eingelesen, wobei der Speicherverbrauch im Verhältnis zur Filegröße zu vernachlässigen ist.
Z.B. verbraucht ein 500MB File zwischen 100 - 180 MB RAM, oder ein 200MB File um die 60 MB RAM.

Update für TraceEye - Professional LogViewer folgt die Tage....