Autor |
Beitrag |
cbs
      
Beiträge: 207
Erhaltene Danke: 1
|
Verfasst: Sa 15.12.07 13:43
Hallo,
Ich habe eine Datei die ein paar Millionen Werte enthält (kann stark varieren). Diese Werte möchte ich, um sie auszuwerten und weiterzuverabrbeiten, in genausoviele Objekte laden.
Das ist soweit kein Problem, dauert aber mitunter ein paar Minuten.
Im wesentlichen handelt es sich um folgende Struktur:
-Datum (8 Byte)
-Ganzzahl (1 Byte)
-Ganzzahl (4 Byte)
-Ganzzahl (4 Byte)
Bisher habe ich die TFileStream Klasse und die Methode ReadBuffer verwendet um die Werte zu lesen. Inszwischen kopiere ich die gesamte Datei mit CopyFrom in ein TMemoryStream Objekt bevor ich die Werte einlese, was einen Geschwindigkeitsvorteil bringt.
Meine Frage ist nun, ob ich das Einlesen irgendwie noch weiter beschleunigen kann. Irgendwie puffern, cachen oder was auch immer.
Ich opfere gern Arbeitsspeicher um die Geschwindigkeit zu erhöhen.
Danke schon mal!
mfg
cbs
|
|
jakobwenzel
      
Beiträge: 1889
Erhaltene Danke: 1
XP home, ubuntu
BDS 2006 Prof
|
Verfasst: Sa 15.12.07 14:11
Glaskugel: |
Der Fehler liegt in Zeile 666 der Unit blubb.pas. Wenns die nicht gibt, ist dein Delphi kaputt und muss neu installiert werden.
|
Ok, die Glaskugel ist immer noch kaputt...
Ohne deinen Code zu sehen, kann dir leider niemand helfen.
_________________ I thought what I'd do was, I'd pretend I was one of those deaf-mutes.
|
|
cbs 
      
Beiträge: 207
Erhaltene Danke: 1
|
Verfasst: Sa 15.12.07 18:42
Ok verstanden.
Hier der relevante Code. Ich habs eben fix neu geschrieben damit es übersichtlicher ist. Habs nicht getestet aber müsste so funktionieren.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private public end;
TWert = class(TObject) Datum: TDateTime; Flags: Byte; Anzahl: LongInt; Uebereinstimmungen: LongInt; end;
var Form1: TForm1; daten: TList;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject); var streamM: TMemoryStream; streamF: TFileStream; cnt, cntr: Integer; d: TDateTime; b: Byte; i1, i2: Integer; wert: TWert; begin streamM:= TMemoryStream.Create; streamF:= TFileStream.Create('D:\testdaten.data', fmOpenRead or fmShareDenyWrite); if Assigned(daten) = false then begin daten:= TList.Create; end else begin daten.Clear; end; try streamM.CopyFrom(streamF, streamF.Size);
cnt:= Trunc((8 + 1 + 4 + 4) / streamM.Size);
streamM.Position:= 0; for cntr := 0 to cnt - 1 do begin streamM.ReadBuffer(d, 8); streamM.ReadBuffer(b, 1); streamM.ReadBuffer(i1, 4); streamM.ReadBuffer(i2, 4); wert:= TWert.Create; wert.Datum:= d; wert.Flags:= b; wert.Anzahl:= i1; wert.Uebereinstimmungen:= i2; daten.Add(wert); end;
finally streamM.Free; streamF.Free; end; end;
end. |
Auf die Art lese ich diese und andere Dateien ein. Wie gesagt sind es teilweise mehrere Millionen Einträge. Um Arbeitsspeicher mache ich mir keine Sorgen, solange es nicht in die GB geht  . Mir gehts primär um die Funktion und da möchte ich nicht bei jeder Datei Minuten lang warten bis der Einlese-Prozess abgeschlossen ist.
Drum: Gibt es eine Möglichkeit wie ich das ganze Beschleunigen kann?
Danke euch!
mfg
cbs
|
|
Allesquarks
      
Beiträge: 510
Win XP Prof
Delphi 7 E
|
Verfasst: Sa 15.12.07 18:58
Also so überschlagsweise solltest du pro Datensatz etwa 20 Byte verbrauchen und das 1 Million mal. Macht 20 MB total moderat ungefähr 1 sekunde zum einlesen von der Platte.
Aber ich denke es ist keine gute Idee für jeden Datensatz eine Instanz anzulegen. Denn die werden alle dynamisch erzeugt und alle einzeln. Das heißt du forderst ne Million mal an. Erstens kann das zu erheblicher fragmentierung führen (das ist dann später beim Zugriff auch langsamer) aber Speicher anfordern ist sowieso langsam. und Objekte erzeugen ebenfalls nochmal. Ich würde dir empfehlen deine "Klasse" in der du kein objektorientiertes Konzept benutzt zu einem record zu machen und ein array of Myrecord zu benutzen, was du einmal!! am Anfang auf die richtige Länge setzt.
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: So 16.12.07 12:59
Hallo,
dieses langsame Verhalten liegt aber an delphi.
Freepascal 2.2.0 ist erheblich schneller:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148:
| (als "Test.dpr" gespeichert) program Test;
{$APPTYPE console} uses sysutils,classes; const MaxZahl = 500000; MeineDatei='C:\MeineDaten.data';
type
TWertRec = packed record Datum: TDateTime; Flags: Byte; Anzahl: LongInt; Uebereinstimmungen: LongInt; end;
TWert = class(TObject) Datum: TDateTime; Flags: Byte; Anzahl: LongInt; Uebereinstimmungen: LongInt; end;
var t1,t0: TDAteTime; WerteFeld : array of TWertRec;
daten: TList;
procedure Erzeugen; var i : integer; Wert: tWertRec; WerteDatei : file of tWertRec;
begin AssignFile(WerteDatei,MeineDatei); rewrite(WerteDatei); randomize; For i := 1 to MaxZAhl do begin with wert do begin Datum:= now; Flags:= random(256); Anzahl:= i; Uebereinstimmungen:= random(Anzahl+1); IF MAxZahl< 1024 then Writeln(I:10,FormatDateTime(' DD.MM.YYYY hh:mm:ss.zzz',Datum),Flags:4,Anzahl:12,Uebereinstimmungen:12) else IF (I And $ffFF) = 0 then Writeln(I:10,FormatDateTime(' DD.MM.YYYY hh:mm:ss.zzz',Datum),Flags:4,Anzahl:12,Uebereinstimmungen:12);
end; write(WerteDatei,Wert); end; CloseFile(WerteDatei); end;
procedure EinlesenArray; Var cnt:integer; WerteDatei : file of tWertRec; begin AssignFile(WerteDatei,MeineDatei); reset(WerteDatei); cnt :=filesize(WerteDatei); writeln('Anzahl Datensätze: ',cnt); setlength(WerteFeld,cnt); BlockRead(WerteDatei,WerteFeld[0],cnt); closefile(WerteDatei); end;
procedure EinlesenObject; var streamM: TMemoryStream; streamF: TFileStream; cnt, cntr: Integer; d: TDateTime; b: Byte; i1, i2: Integer; wert: TWert; begin streamM:= TMemoryStream.Create; streamF:= TFileStream.Create(MeineDatei, fmOpenRead or fmShareDenyWrite); if Assigned(daten) = false then begin daten:= TList.Create; end else begin daten.Clear; end; try streamM.CopyFrom(streamF, streamF.Size);
cnt:= streamM.Size DIV (SizeOf (Twertrec)); writeln('Anzahl Datensätze: ',cnt);
streamM.Position:= 0; Daten.count:= cnt; for cntr := 0 to cnt - 1 do begin wert:= TWert.Create; with wert do begin streamM.ReadBuffer(datum,SizeOf(Datum)); streamM.ReadBuffer(Flags, SizeOf(Flags)); streamM.ReadBuffer(Anzahl, SizeOf(Anzahl)); streamM.ReadBuffer(Uebereinstimmungen,SizeOf(Uebereinstimmungen)); end; daten[cntr]:=wert; IF (cntr And $FFFF) = 0 then with Twert(daten[cntr]) do Writeln(cntr:10,FormatDateTime(' DD.MM.YYYY hh:mm:ss.zzz',Datum),Flags:4,Anzahl:12,Uebereinstimmungen:12);
end;
Writeln(Daten.count); finally streamM.Free; streamF.Free; end; end;
begin T0:= now; EinlesenArray; T1:= now; setlength(WerteFeld,0); Writeln(FormatDateTime(' hh:mm:ss.zzz',T1-T0));
T0:= now; EinlesenObject; T1:= now; Writeln(FormatDateTime(' hh:mm:ss.zzz',T1-T0)); readln; End. |
Mit Delphi7(Nutzung von FastMM4 Ohne Unterschied)
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| Anzahl Datensätze: 500000 00:00:00.030 Anzahl Datensätze: 500000 0 16.12.2007 11:28:06.797 186 1 1 65536 16.12.2007 11:28:06.998 184 65537 38021 131072 16.12.2007 11:28:07.208 182 131073 33876 196608 16.12.2007 11:28:07.408 180 196609 184177 262144 16.12.2007 11:28:07.619 178 262145 161241 327680 16.12.2007 11:28:07.829 176 327681 96140 393216 16.12.2007 11:28:08.029 174 393217 382093 458752 16.12.2007 11:28:08.239 172 458753 298201 500000 00:00:01.652 |
Mit Freepascal
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| ■ Free Pascal IDE Version 1.0.10 [2007/09/09] ■ Compiler Version 2.2.0 ■ GBD Version GDB 6.2.1 ■ Cygwin "C:\FPC\2.1.4\bin\i386-win32\cygwin1.dll" version 1005.18.0.0 Running "c:\test.exe " Anzahl Datensätze: 500000 00:00:00.060 Anzahl Datensätze: 500000 0 16.12.2007 11:28:06.797 186 1 1 65536 16.12.2007 11:28:06.998 184 65537 38021 131072 16.12.2007 11:28:07.208 182 131073 33876 196608 16.12.2007 11:28:07.408 180 196609 184177 262144 16.12.2007 11:28:07.619 178 262145 161241 327680 16.12.2007 11:28:07.829 176 327681 96140 393216 16.12.2007 11:28:08.029 174 393217 382093 458752 16.12.2007 11:28:08.239 172 458753 298201 500000 00:00:00.320 |
Mit 5 Mio Datensätzen nur Freepascal, Delphi7 dauerte mir zu lange...
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| Running "c:\test.exe " Anzahl Datensätze: 5000000 00:00:00.450 Anzahl Datensätze: 5000000 0 16.12.2007 11:35:41.742 88 1 1 1048576 16.12.2007 11:35:45.387 131 1048577 953161 2097152 16.12.2007 11:35:49.012 183 2097153 793167 3145728 16.12.2007 11:35:52.627 225 3145729 2012977 4194304 16.12.2007 11:35:56.252 35 4194305 451753 5000000 00:00:02.464 |
Das ist ja ein merkwürdig großer Unterschied
Gruß Horst
|
|
hathor
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 16.12.07 13:03
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: So 16.12.07 13:54
Hallo,
RAMDISK , Speicher ist durch nichts zu ersetzen.
Ich habe bei mir festgestellt, das die Auslagerungsdatei bei 5 Mio Datensätzen (85 MB) beansprucht wird( 512 MB Hauptspeicher WinXP , Notebook mit Sparmodi, das heisst, dass nach ein paar Zehntelsekunden der Takt von 600Mhz auf 1700 erhöht wird))
Deshalb habe ich die Datenobjektliste ohne File- und memory-stream eingelesen:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44:
| procedure EinlesenObject; var cnt, cntr: Integer; WertRec: TWertrec; WerteDatei : file of tWertRec;
begin Assign(WerteDatei,MeineDatei); reset(WerteDatei); if Assigned(daten) = false then begin daten:= TList.Create; end else begin daten.Clear; end; try cnt:= fileSize(WerteDatei); writeln('Anzahl Datens„tze: ',cnt); readln; Daten.count:= cnt; for cntr := 0 to cnt - 1 do begin wert:= TWert.Create; Read(WerteDatei,WertRec); with wert do begin datum:= WertRec.Datum; Flags:= WertRec.Flags; Anzahl:= WertRec.Anzahl; Uebereinstimmungen:= WertRec.Uebereinstimmungen; end; daten[cntr]:=wert; IF (cntr And $FFFFF) = 0 then with Twert(daten[cntr]) do Writeln(cntr:10,FormatDateTime(' DD.MM.YYYY hh:mm:ss.zzz',Datum),Flags:4,Anzahl:12,Uebereinstimmungen:12); end;
Writeln(Daten.count); finally closeFile(WerteDatei); end; end; |
delphi7:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| Anzahl Datensätze: 5000000 00:00:00.791 Anzahl Datensätze: 5000000
0 16.12.2007 11:35:41.742 88 1 1 1048576 16.12.2007 11:35:45.387 131 1048577 953161 2097152 16.12.2007 11:35:49.012 183 2097153 793167 3145728 16.12.2007 11:35:52.627 225 3145729 2012977 4194304 16.12.2007 11:35:56.252 35 4194305 451753 5000000 00:00:06.960 |
Freepascal:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| ■ Free Pascal IDE Version 1.0.10 [2007/09/09] ■ Compiler Version 2.2.0 ■ GBD Version GDB 6.2.1 ■ Cygwin "C:\FPC\2.1.4\bin\i386-win32\cygwin1.dll" version 1005.18.0.0 Running "c:\test.exe " Anzahl Datensätze: 5000000 00:00:00.370 Anzahl Datensätze: 5000000
0 16.12.2007 11:35:41.742 88 1 1 1048576 16.12.2007 11:35:45.387 131 1048577 953161 2097152 16.12.2007 11:35:49.012 183 2097153 793167 3145728 16.12.2007 11:35:52.627 225 3145729 2012977 4194304 16.12.2007 11:35:56.252 35 4194305 451753 5000000 00:00:05.538 |
Delphi wird nun wesentlich schneller.
Es liegt also an der zu späten Freigabe des FileStreams (hätte nach dem Kopieren in den Memorystream freigegeben werden können) und des Speicherplatzes des Memorystreams.
Liest man die Daten mit read statt mit BlockRead , dauert das Einlesen Array ~4,6 Sekunden. Das Erstellen der 5 Mio Objekte in der Liste und kopieren von WertRec in Wert dauert nur ca eine Sekunde.
Bei so wenig Daten pro Objekt ist der Overhead gewaltig. 178 Mb statt 85 Mb werden belegt.
Was soll's. Nutzung eines Feldes und laden mittels Blockread ist mit Abstand das schnellste und Resourcen sparend.
Gruß Horst
|
|
cbs 
      
Beiträge: 207
Erhaltene Danke: 1
|
Verfasst: Mi 19.12.07 12:12
Ich danke euch, das hat mir sehr geholfen!
mfg
cbs
|
|
|