Autor |
Beitrag |
Ronald
Hält's aus hier
Beiträge: 8
|
Verfasst: Do 03.07.14 09:48
Hallo Zusammen,
ich brauch mal Hilfe. Ich arbeite als Controller bei einem großen Handelshaus.
Dort werden in BW artikelgenaue Daten für unser MIS zu Warengruppen aggregiert ausgelesen, die wir in unser MIS importieren. Jetzt haben wir beginnend mit 2014 die Rechnungsgrundlage für die Margenermittlung umgestellt. Dies läßt sich im BW auf artikelebene nicht nachbauen, aber in unserem MIS geht das. Die Daten bekomme ich als sehr große Flat-Files (Die Datei kann nicht mal mit Word geöffnet werden).
Jetzt muss ich aber für die Historie die Umbewertungen nachrechnen und zusätzlich in das MIS laden, so dass wir wenigsten hier einen sinnvollen Vorjahresvergleich bei den Margen abbilden können. Ist ja wichtig für die Unternehmenssteuerung. Das heißt: Die großen Flatfiles einlesen, die umzubewertenden Kostenarten ermitteln und die Korrekturwerte in eine neuen Flatfile schreiben. Die Dateien sind leider in Summe so groß, dass dies nicht komplett im Arbeitsspeicher geht. Also habe ich den so ungeliebten langsamen File-IO verwendet. Läuft ewig, aber funktioniert. Leider ist dieser ganze Prozess maximal Zeitkritisch und ich suche daher nach Optimierungpotential in der Laufzeit (aktuell ca 4-6 Stunden). Und diesen Prozess müssen wir jedesmal von neuem durchführen, wenn die Warenfachhierarchie von den verantwortlichen geändert wird.
Ich suche nach einer Lösung, die mir Laufzeit einspart. Leider kommt die Verarbeitung der kompletten Datei im Arbeitsspeicher nicht in Frage. Ein Aufsplitten der Datei in mehrere Kleinere bringt von der Laufzeit durch vorgelagerte Prozesse auch keinen Performancevorteil. Ich hab das Programm mal angehängt
Die Daten liegen mir als CSV-Datei vor:
414;1231;A;2;AOVKWO;I;6;2012;Eur;179,97
414;1231;A;2;AOGLD;I;6;2012;Eur;137,62
414;1291;A;16;EVVKWIHW;I;5;2012;Eur;0,19
414;1291;A;16;EVGLDHW;I;5;2012;Eur;0,15
414;1231;B;2;AOVKWI;I;6;2012;Eur;228,63
414;1231;B;2;AOVKWO;I;6;2012;Eur;213,66
414;1231;B;2;AOGLD;I;6;2012;Eur;141,05
414;1231;E;2;AOVKWI;I;6;2012;Eur;19,90
414;1231;E;2;AOVKWO;I;6;2012;Eur;18,60
414;1231;E;2;AOGLD;I;6;2012;Eur;13,34
414;1231;M;2;AOVKWI;I;6;2012;Eur;1117,60
414;1231;M;2;AOVKWO;I;6;2012;Eur;1044,50
414;1231;M;2;AOGLD;I;6;2012;Eur;687,52
414;1231;P;2;AOVKWI;I;6;2012;Eur;104,76
Einloggen, um Attachments anzusehen!
|
|
chrisw
Beiträge: 439
Erhaltene Danke: 3
W2K
D7
|
Verfasst: Do 03.07.14 11:04
Spricht etwas dagegen die csv-Datei in eine Datenbank (MYSQL, PostgreSQL, ...) zu importieren, denke dann kannst du jegliche SQL Abfrage in angebrachter Zeit starten.
ein Beispiel für einen Import zu MYSQL
SQL-Anweisung 1: 2: 3: 4: 5: 6: 7: 8:
| LOAD DATA LOCAL INFILE 'csvTest.csv' INTO TABLE csvImport FIELDS TERMINATED BY ';' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES (field1, @dummy, field2, @rechenVar) SET field3 = @rechenVar*10 |
Christian
_________________ Man sollte keine Dummheit zweimal begehen, die Auswahl ist schließlich groß genug.
|
|
OlafSt
Beiträge: 486
Erhaltene Danke: 99
Win7, Win81, Win10
Tokyo, VS2017
|
Verfasst: Do 03.07.14 11:04
Über welche Dateigrößen reden wir hier ? Irgendwann ist jede Textdatei langsam, sie muß nur groß genug sein
_________________ Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
|
|
Ronald
Hält's aus hier
Beiträge: 8
|
Verfasst: Do 03.07.14 11:56
Also die Dateien sehen so aus
Dateigröße: 3,6 GB zum Beispiel. Liegen alle so in diesem Korridor
Das mit der Datenbank probier ich mal. War gerade bei der IT. Die richten mir eine Tabelle ein, auf die ich vollzugriff habe. Mal sehen, ob ich meine Ziele über einen Select erreiche. Andernfall muss ich mal sehen, wie ich die Daten connecte. Hab das schon ewig nicht mehr gemacht.
Bin halt so ein Problemlöser-Entwickler für Nischen im Controlling für Sachen, die mir die IT nicht liefern kann, wie ich sie brauche. Kommt halt eher selten vor, aber wenn dann wichtig ^^
_________________ Performance ist durch nicht zu ersetzen außer durch Performance
|
|
OlafSt
Beiträge: 486
Erhaltene Danke: 99
Win7, Win81, Win10
Tokyo, VS2017
|
Verfasst: Do 03.07.14 12:36
Also um die 4GB. Da wundert mich schon allerhand nicht mehr
Sämtliche Delphi-Bordmittel wie TStringList oder TMemo sind mit allem, was mehr als ~20k Zeilen (!) enthält, überfordert. Solche Files kann man ggf. noch mit einem Compiler-Editor a la Visual C# bearbeiten - aber bei 32Bit und deinen Dateigrößen ist auch das unmöglich (max 3GB Speicher allozierbar).
Ich würde die Datei zeilenweise einlesen und verarbeiten:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| var f: TextFile; TheLine: string; begin AssignFile(f,'TheFile.CSV'); Reset(f); while not eof(f) do begin ReadLn(f,TheLine); end; CloseFile(f); end; |
Eine andere Möglichkeit sehe ich nicht, um das ganze in beherrschbaren Zeiträumen zu halten.
_________________ Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
|
|
jasocul
Beiträge: 6388
Erhaltene Danke: 146
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Do 03.07.14 12:38
Schmeiß mal deine Progressbar raus. Die aktualisiert bei jeder gelesenen Zeile. Das kostet bei dieser Dateigröße richtig Zeit.
Und wenn du die unbedingt brauchst, dann aktualisiere nur bei jeder 1000sten Zeile. Ich hatte schon Befürchtungen, dass da irgendwo ein Application.ProcessMessages steht. Ein absoluter Performance-Killer.
Wobei ich denke, dass das Bulk-Insert in die Datenbank trotzdem die bessere Lösung sein wird.
@ OlafSt:
Schau dir doch erstmal den Source an. Das wird schon so gemacht.
|
|
jaenicke
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 03.07.14 13:12
Der größte Performancekiller ist dort das Readln. Das ist nämlich erst einmal sehr langsam. Sinnvoller ist die Verwendung von MMFs, die genau für so große Dateien ideal sind. Eine entsprechende Kapselung findest du von mir hier:
www.entwickler-ecke....+Reader_99933,0.html
Damit ist dann auch gleich die äußerst ungünstig implementierte Fortschrittsanzeige erledigt, denn Position und Größe bekommst du dort neben einem zeilenweisen Auslesen direkt...
Dazu kommt dann noch wie schon erwähnt wurde, dass die Fortschrittsanzeige nicht bei jedem Durchlauf aktualisiert werden sollte.
Am besten wäre das ganze direkt in einen Thread zu packen. Dann kannst du die aktuelle Position mit Hilfe einer atomaren Funktion ( AtomicExchange, ...) in ein von außen erreichbares Feld schreiben und das nur periodisch aktualisieren.
Mehr kann ich aktuell nicht zeigen, da ich das unterwegs am Handy schreibe.
Für diesen Beitrag haben gedankt: Martok
|
|
Ronald
Hält's aus hier
Beiträge: 8
|
Verfasst: Do 03.07.14 13:21
Ok, das mit dem Progressbar hatt ich gar nicht so auf dem Schirm.
Ich setz mal bei Euren Vorschlägen an und probier das mal aus.
Vielen Dank
_________________ Performance ist durch nicht zu ersetzen außer durch Performance
|
|
Ronald
Hält's aus hier
Beiträge: 8
|
Verfasst: Do 03.07.14 15:40
Also, der relevante Datenbestand eines Jahres ist jetzt in einer Tabelle des MSSQL-Servers.
Ca. 97 Mio Datensätze
_________________ Performance ist durch nicht zu ersetzen außer durch Performance
|
|
Teekeks
Beiträge: 211
Erhaltene Danke: 23
|
Verfasst: Do 03.07.14 20:05
MSSQL ist so ziemlich das schlimmste was du machen kannst bei vielen Datensätzen.
Nach den Tests die ich gemacht habe, würde ich Firebird nutzen, das verliert fast nix an Geschwindigkeit bei seeeehr vielen Datensätzen (auch wenn es nicht soo schnell ist bei wenigen Daten)
|
|
jaenicke
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 03.07.14 20:34
Wobei es da auch noch massive Unterschiede zwischen den Zugriffsbibliotheken gibt. ZEOS war z.B. bei MS SQL in meinen Tests massiv langsamer als FireDAC, insbesondere bei großen Tabellen, bei Firebird war der Unterschied z.B. deutlich kleiner.
|
|
GuaAck
Beiträge: 376
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: Fr 04.07.14 21:52
Hallo Alle,
das Problem von Ronald ist doch, dass es eine einmalige Aktion ist, so wie ich seine Frage verstanden habe. Da darf man doch nicht viel Arbeit in Datenbanken usw. stecken.
Ich hatte letzten Messwertreihen nach bestimmten sehr kurzzeitigen Ereignissen zu durchsuchen und die Messwerte statistisch auszuwerten. Die Daten habe ich als CSV bekommen, waren etwa 100 GByte.
Die Daten lagen in Einzeldateien fortlaufend numeriert mit einer Größe von je einigen 100 MB vor. Diese Dateien habe ich zunächst in Binärdaten gewandelt (das dauerte ein paar Stunden, aber es sparte für die folgende Auswertung das dauernde Parsen der CSV. (Interessanterweise waren die Binärdateien größer als die CSV-Dateien, was an der Art der Daten liegt.) Ab dann dauerte jede Auswertung nur wenige Sekunden, selbst ein Plott über die gesamte Zeit war in wenigen Minuten gemacht. (PC: Single Core 2,2 GHz, also nicht ganz neu).
Ich würde also:
a) Dateien splitten z. B. in 500 MB-Pakete
b) Die Datei in Binärdaten wandeln
c) Die folgenden Zuordnung der Artikel zu Warengruppen einfach sequentiell abarbeiten.
MySQL hatte ich für diese Aufgabe auch untersucht, ging sehr gut, Performance für sehr große Datenmengen habe ich nicht getestet.
Vielleicht hilft das ja,
Gruß
GuaAck
|
|
jaenicke
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 05.07.14 05:47
|
|
Horst_H
Beiträge: 1653
Erhaltene Danke: 243
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: So 06.07.14 00:37
Hallo,
ich bin mir nicht sicher, ob Ronalds Version nicht sehr umständlich vorgeht.
Felder werden zum Teil nicht als Referenz übergeben, das ist natürlich viel unnötige Arbeit.
Auch das Case Kontrukt besteht wohl aus Copy&Paste. Freepascal konnte es aber gar nicht umsetzen, (EDIT) aber unter Lazarus funktioniert es.
Die Datei wurde zuerst vollständig eingelesen, um die Zeilen zu zählen???
Jeder Wert wurde viermal geparst,Statt einmal eine Zeile und dann kopieren.
Das wichtigste ist aber die Ein/Ausgabe:
Die Erhöhung des Pufferspeichers ( hier 32 KB ( 64 geht auch, ist aber nicht schneller ) ) hilft enorm:
EDIT:
FloatToStr ist langsam FloatToStrF wesentlcih schneller ( rundet selbst ) und MyFloatToStr4 noch etwas.
Dabei werden alle möglichen Nachkommazahlen vorab gespeichert.
real 0m21.609s sind jetzt 276000 Zeilen/s ( statt 188000/s ) Eingabe und 830000 Zeilen/s Ausgabe
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: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502:
| program unbenannt; {$IFNDEF FPC} {$APPTYPE CONSOLE} {$ENDIF}
uses sysutils,classes,crt; const ZehnHoch4 :double = 10000.0; RezZehnHoch4:double = 1/10000.0;
maxStringCnt = 5; decSep = ','; type tNachkomma4 = array[0..4] of char; tArrNachkomma = array[0..9999] of tNachkomma4; tMyString = string; tpChar = ^char; tarr = Array[1..10] of tMyString; tStringsIdx = 1..maxStringCnt; const VergleichsStrings : array[tStringsIdx] of tMyString = ('AOVKWO','AOGLD','EVVKWIHW','EVGLDHW','AOVKWI'); ErsatzLogistrings : array[tStringsIdx] of tMyString = ('Logi01','Logi02','Logi03','Logi04','Logi05'); ErsatzVewastrings : array[tStringsIdx] of tMyString = ('Vewa01','Vewa02','Vewa03','Vewa04','Vewa05');
FaktorNetto : double = 1.047; FaktorLogistik : double = 0.0375; var Nachkomma :tArrNachkomma; dFaktorNetto : double; dFaktorLogistik: double; fIn, fOut : TextFile; sPath : string;
function ParseSem(var S: tMyString): tArr; const delimiterChar = ';'; var pC : tpChar; aktPos,letztPos,k: Integer; begin pC := @S[1]; dec(pC); aktPos := 1; letztPos :=1; k := 1; repeat if pC[Aktpos] = delimiterChar then begin result[k]:=copy(S,letztPos,Aktpos-letztPos); LetztPos := Aktpos+1; inc(k); end; inc(Aktpos); until Aktpos>= length(S); result[k]:=copy(S,letztPos,Aktpos-letztPos+1); end;
function RundeAuf4Stellen(zahl: double): double; const ZehnHoch4 :double = 10000.0; RezZehnHoch4:double = 1/10000.0; begin result := round(zahl*ZehnHoch4)*RezZehnHoch4; end;
function MyFloatToStr4(d : double):string; var len,i : integer; begin d := round(d*ZehnHoch4)*RezZehnHoch4; i := trunc(d); result := IntToStr(i); len := length(result)+1; setlength(result,len+4); i := trunc(abs(d-i)*ZehnHoch4+0.5); Move(NachKomma[i],result[len],5); end;
function CreateOutputS(var arrOut: tarr):tMyString; var i: integer; begin result := ''; for i := 1 to 10 do result := result+ arrOut[i] + ';'; setlength(result,Length(result)-1); end;
Procedure ConcatAusgabe(var s: tMyString); begin writeln(fOut,s); end;
procedure SchreibeArray(var arrOut: tarr); var sOut: tMyString; begin sOut:=CreateOutputS(arrOut); ConcatAusgabe(sOut); end;
function CheckVergleich(var s:tMyString): integer; var i : integer; begin result := 0; For i := Low(tStringsIdx) to High(tStringsIdx) do begin IF s = VergleichsStrings[i] then begin result := i; Break; end; end; end;
procedure Button1Click; var dWert, dLogistik, dVerwaltung, dNetto, dAufschlag : Double;
arrSource : tarr; arrNew : tarr; arrLogistik : tarr; arrVerwaltung : tarr; s, fName : tMyString; InBuf, OutBuf : array of Char; i,rowcnt: integer;
procedure SchreibeArrs(i: integer); begin IF i in [Low(VergleichsStrings)..High(VergleichsStrings)] then begin arrVerwaltung[5] := ErsatzVewastrings[i]; arrLogistik[5] := ErsatzLogistrings[i]; SchreibeArray(arrNew); SchreibeArray(arrVerwaltung); SchreibeArray(arrLogistik); end; end;
begin dFaktorNetto := FaktorNetto; dFaktorLogistik := FaktorLogistik;
fName := 'Test.csv'; assignfile(fIn,fName); reset(fIn); fName := 'Ausgabe.csv'; AssignFile(fOut,fName); setlength(InBuf,1 shl 15); SetTextBuf(fIn,InBuf[0],length(InBuf));
rewrite(fOut); setlength(OutBuf,1 shl 15); SetTextBuf(fOut,OutBuf[0],length(OutBuf)); reset(fIn);
rowcnt := 0; while not eof(fIn) do begin readln(fIn,s);
arrSource:= parseSem(s); arrNew :=arrSource; arrLogistik :=arrSource; arrVerwaltung :=arrSource;
dWert := StrToFloat(arrSource[10]); dNetto := dWert / dFaktorNetto; dAufschlag := dWert - dNetto; dLogistik := dNetto * dFaktorLogistik; dVerwaltung := dAufschlag - dLogistik; dWert := dNetto + dLogistik; arrLogistik[10] := MyFloatToStr4(-dLogistik); arrVerwaltung[10] := MyFloatToStr4(-dVerwaltung); i:= CheckVergleich(arrSource[5]); IF i <> 0 then SchreibeArrs(i) else ConcatAusgabe(s); inc(rowcnt); end; closefile(fIn); flush(fOut); closeFile(fOut); setlength(OutBuf,0); setlength(InBuf,0);
writeln('Bearbeitung von ',rowcnt,' Zeilen abgeschlossen'); end; var i : integer; f : double; Begin DecimalSeparator :=decSep; For i := 0 to 9999 do NachKomma[i] := Format(',%.4d',[i]); writeln( NachKomma[0]); writeln(MyFloatToStr4(-1234.56789));
Button1Click END.
{$IFNDEF FPC} {$APPTYPE CONSOLE} {$ENDIF}
uses sysutils,classes,crt; const ZehnHoch4 :double = 10000.0; RezZehnHoch4:double = 1/10000.0;
maxStringCnt = 5; decSep = ','; type tNachkomma4 = array[0..4] of char; tArrNachkomma = array[0..9999] of tNachkomma4; tMyString = string; tpChar = ^char; tarr = Array[1..10] of tMyString; tStringsIdx = 1..maxStringCnt; const VergleichsStrings : array[tStringsIdx] of tMyString = ('AOVKWO','AOGLD','EVVKWIHW','EVGLDHW','AOVKWI'); ErsatzLogistrings : array[tStringsIdx] of tMyString = ('Logi01','Logi02','Logi03','Logi04','Logi05'); ErsatzVewastrings : array[tStringsIdx] of tMyString = ('Vewa01','Vewa02','Vewa03','Vewa04','Vewa05');
FaktorNetto : double = 1.047; FaktorLogistik : double = 0.0375; var Nachkomma :tArrNachkomma; dFaktorNetto : double; dFaktorLogistik: double; fIn, fOut : TextFile; sPath : string;
function ParseSem(var S: tMyString): tArr; const delimiterChar = ';'; var pC : tpChar; aktPos,letztPos,k: Integer; begin pC := @S[1]; dec(pC); aktPos := 1; letztPos :=1; k := 1; repeat if pC[Aktpos] = delimiterChar then begin result[k]:=copy(S,letztPos,Aktpos-letztPos); LetztPos := Aktpos+1; inc(k); end; inc(Aktpos); until Aktpos>= length(S); result[k]:=copy(S,letztPos,Aktpos-letztPos+1); end;
function RundeAuf4Stellen(zahl: double): double; const ZehnHoch4 :double = 10000.0; RezZehnHoch4:double = 1/10000.0; begin result := round(zahl*ZehnHoch4)*RezZehnHoch4; end;
function MyFloatToStr4(d : double):string; var len,i : integer; begin d := round(d*ZehnHoch4)*RezZehnHoch4; i := trunc(d); result := IntToStr(i); len := length(result)+1; setlength(result,len+4); i := trunc(abs(d-i)*ZehnHoch4+0.5); Move(NachKomma[i],result[len],5); end;
function CreateOutputS(var arrOut: tarr):tMyString; var i: integer; begin result := ''; for i := 1 to 10 do result := result+ arrOut[i] + ';'; setlength(result,Length(result)-1); end;
Procedure ConcatAusgabe(var s: tMyString); begin end;
procedure SchreibeArray(var arrOut: tarr); var sOut: tMyString; begin sOut:=CreateOutputS(arrOut); ConcatAusgabe(sOut); end;
function CheckVergleich(var s:tMyString): integer; var i : integer; begin result := 0; For i := Low(tStringsIdx) to High(tStringsIdx) do begin IF s = VergleichsStrings[i] then begin result := i; Break; end; end; end;
procedure Button1Click; var dWert, dLogistik, dVerwaltung, dNetto, dAufschlag : Double;
arrSource : tarr; arrNew : tarr; arrLogistik : tarr; arrVerwaltung : tarr; s, fName : tMyString; InBuf, OutBuf : array of Char; i,rowcnt: integer;
procedure SchreibeArrs(i: integer); begin IF i in [Low(VergleichsStrings)..High(VergleichsStrings)] then begin arrVerwaltung[5] := ErsatzVewastrings[i]; arrLogistik[5] := ErsatzLogistrings[i]; SchreibeArray(arrNew); SchreibeArray(arrVerwaltung); SchreibeArray(arrLogistik); end; end;
begin dFaktorNetto := FaktorNetto; dFaktorLogistik := FaktorLogistik;
fName := 'Test.csv'; assignfile(fIn,fName); reset(fIn); fName := 'Ausgabe.csv'; AssignFile(fOut,fName); setlength(InBuf,1 shl 15); SetTextBuf(fIn,InBuf[0],length(InBuf));
rewrite(fOut); setlength(OutBuf,1 shl 15); SetTextBuf(fOut,OutBuf[0],length(OutBuf)); reset(fIn);
rowcnt := 0; while not eof(fIn) do begin readln(fIn,s);
arrSource:= parseSem(s); arrNew :=arrSource; arrLogistik :=arrSource; arrVerwaltung :=arrSource;
dWert := StrToFloat(arrSource[10]); dNetto := dWert / dFaktorNetto; dAufschlag := dWert - dNetto; dLogistik := dNetto * dFaktorLogistik; dVerwaltung := dAufschlag - dLogistik; dWert := dNetto + dLogistik; arrLogistik[10] := MyFloatToStr4(-dLogistik); arrVerwaltung[10] := MyFloatToStr4(-dLogistik); i:= CheckVergleich(arrSource[5]); IF i <> 0 then SchreibeArrs(i) else ConcatAusgabe(s); inc(rowcnt); end; closefile(fIn); flush(fOut); closeFile(fOut); setlength(OutBuf,0); setlength(InBuf,0);
writeln('Bearbeitung von ',rowcnt,' Zeilen abgeschlossen'); end; var i : integer; f : double; Begin DecimalSeparator :=decSep; For i := 0 to 9999 do NachKomma[i] := Format(',%.4d',[i]); writeln( NachKomma[0]); writeln(MyFloatToStr4(-1234.56789));
Button1Click END. |
Wären es tatsächlich nur 5 Sekunden für 700 Mb, kämen 140 Mb/s zustande, aber das Betriebssystem verwaltet die Festplatte und ist auch während der Berechnung tätig.
So sind es nur 21 Mb/s fürs schreiben.
Da aber ca 26 Sekunden nur Verarbeitungszeit sind kann man ja etwas abschätzen.
8,8 MB == 230000 Zeilen/s werden verarbeitet. So schnell sollte jede Platte lesen können.
Am besten zwei verschiedene physikalische Festplatten.Von einer nur Lesen zur anderen nur schreiben.
Wenn 700 Mb schreiben 5 sekunden zusätzlich kosten dürften 230 MB lesen auch nur knapp 2 Sekunden dauern.
Sind zusammen dann grob 40 Sekunden und auf 93 Mio Datensätze skaliert:
40 *93/6(,02) = 620 Sekunden.= 0,173 h = 188000 Zeilen/s
Gruß Horst
P.S.
Ich habe den Rechner neu gestartet:
real 0m38.108s ist doch ganz gut geschätzt
P.S.S:
Concat sollte vorher mal 256 Ausgabezeilen speichern und dann in einem Rutsch ausgeben.Das brachte nur wenig.
EDIT:
Unter Lazarus funktioniert system.SetTextBuf und vielleicht statt TextFile nur Text.
Es ist sogar fast genauso schnell ( 34 Sekunden ), trotz progressbar alle 1024.
Die Datei habe ich mal angehängt.
Edit2 :
arrVerwaltung[10] := FloatToStrF(-dVerwaltung, ffFixed, 0, 4); ist wesentlich schneller
Dann 28.8 Sekunden
Für Delphi muß
Delphi-Quelltext 1: 2: 3:
| implementation
{$R *.lfm} |
in
Delphi-Quelltext 1: 2: 3:
| implementation
{$R *.dfm} | geändert werden
EDITx :
3,7 GB werden zu 11 GB in = 398 Sekunden
Zitat: |
Bearbeitung von 96337920 Zeilen abgeschlossen
real 6m38.195s
user 4m23.790s
sys 0m6.274s
Mit Turbo Delphi/Win7 :
Bearbeitung von 48168960 Zeilen abgeschlossen
00:02:13.568
= 360000 Zeilen/s
5852528640 Bytes Ausgabe
Mit Turbo Delphi:
Bearbeitung von 48168960 Zeilen abgeschlossen
00:02:13.568
= 360000 Zeilen/s aka 43816847 Byte/s
*
Freepascal 2.6.4 Linux auf EXT4 Platte
Bearbeitung von 48168960 Zeilen abgeschlossen
00:02:23.261
= 336000 Zeilen/s aka 40852211 Byte/s
|
Windows hat wohl den bessernen NTFS-Treiber
Einloggen, um Attachments anzusehen!
|
|
Horst_H
Beiträge: 1653
Erhaltene Danke: 243
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mo 07.07.14 19:07
Hallo,
sodele, jetzt hochspezialisiert, denn Strings zusammenzusetzen kostet viel Zeit.
Zitat: | Bearbeitung von 48168960 Zeilen abgeschlossen
00:00:57.652 |
Also ca. 2 min für 96 Mio Datensätze = 800x1000 1/s
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: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374:
| program ConvCSV;
{$IFNDEF FPC} {$APPTYPE CONSOLE} {$ELSE} {$MODE DELHI} {$H+} {$Optimization ON} {$Optimization Regvar} {$Optimization PeepHole} {$Optimization Asmcse} {$Optimization CSE} {$codealign loop=4,proc=32} {$ENDIF}
uses sysutils,classes; const AnzahlZeilen = 93*1000*1000; EingabeFile = 'Eingabe.csv'; AusgabeFile = 'Ausgabe.csv';
maxSpaltenCnt = 10; delimiterChar = ';'; maxVgStringCnt = 10;
ZehnHoch4 :double = 10000.0; RezZehnHoch4:double = 1/10000.0;
decSep = ',';
type {$IFDEF WideChar } tpC : pWChar; tMyString = widestring; tNachkomma4 = array[0..4] of wchar; tArrNachkomma = array[0..9999] of tNachkomma4; {$ELSE} tpC = pChar; tMyString = string; tNachkomma4 = array[0..4] of char; tArrNachkomma = array[0..9999] of tNachkomma4; {$ENDIF}
tpChar = ^char; tDelimPos = array[0..maxSpaltenCnt] of integer; tarr = Array[1..maxSpaltenCnt] of tMyString; TSpecArr = array[0..1]of tMyString; tCntVgStrings = 1..maxVgStringCnt;
tConvert = record cvFakAufschlag, cvFakLogi : double; cvVgS, cvNewLogi, cvNewVewa: tMyString; end; tConvArr = array[tCntVgStrings] of tConvert;
const FaktorBrutto = 1.047; FaktorAufschlag = (FaktorBrutto -1)/FaktorBrutto ; FaktorLogistik = 0.0375;
cConvArr : tConvArr = ( ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'AOVKWO'; cvNewLogi:'Logi01';cvNewVewa:'Vewa01'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'AOGLD'; cvNewLogi:'Logi02';cvNewVewa:'Vewa02'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'EVVKWIHW'; cvNewLogi:'Logi03';cvNewVewa:'Vewa03'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'EVGLDHW'; cvNewLogi:'Logi04';cvNewVewa:'Vewa04'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'AOVKWI'; cvNewLogi:'Logi05';cvNewVewa:'Vewa05'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'WEKGLDBW'; cvNewLogi:'COWELG2013';cvNewVewa:'COWEVW2013'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'VEGLDBW'; cvNewLogi:'COVBLG2013';cvNewVewa:'COVBVW2013'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'VKGLDBW'; cvNewLogi:'COVKVW2013';cvNewVewa:'COVKLG2013'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'MBGLDBW'; cvNewLogi:'COMBVW2013';cvNewVewa:'COMBLG2013'), ( cvFakAufschlag:FaktorAufschlag;cvFakLogi:FaktorLogistik; cvVgS:'WVGLDBW'; cvNewLogi:'COWVVW2013';cvNewVewa:'COWVLG2013') ); var Nachkomma :tArrNachkomma; DelimPos :tDelimPos;
FrontStr, Checkstr, MiddleStr, WertStr, WertLogi, WertVewa : tMyString;
fIn, fOut : TextFile; InBuf, OutBuf : array of Char; hdAusgabe :boolean;
function MyFloatToStr4(d : double):string; const EinHalb : double =0.5; var len,i : integer; begin d := round(d*ZehnHoch4)*RezZehnHoch4; i := trunc(d); result := IntToStr(i); len := length(result)+1; setlength(result,len+4); i := trunc(abs(d-i)*ZehnHoch4+EinHalb); Move(NachKomma[i],result[len],5); end;
procedure InitNachkomma4; var i : integer; s: string; begin For i := 0 to 9999 do begin s := Format(',%.4d',[i]); move(s[1],NachKomma[i],5); end; s:= ''; end;
procedure CheckMyFloatToStr4; var i : integer; f : double; s: string; begin For i := 1 to 100*1000*1000 do begin f := Random*ZehnHoch4; s := FloatToStrF(f,ffFixed,0,4); IF (MyFloatToStr4(f) <> s) then writeln(i:10,' ',MyFloatToStr4(f),' ',s,' ',f:15:8); end; end;
procedure GenerateDummyFile(rowcnt: integer);
var s,v : string; i: integer; begin rewrite(fIn); i := 0; s := ''; repeat v := Format('414;1231;A;2;%s;I;6;2012;Eur;%7.2f%s', [cConvArr[random(maxVgStringCnt)+1].cvVgS,random*1e4,#13#10]); IF length(v) +Length(s) > length(InBuf) then break; s := s+v; inc(i); until false; repeat write(Fin,s); dec(rowcnt,i); until RowCnt<0; reset(Fin); end;
function OpenIOFiles(FInName,FOutName : String;MinRowCnt: integer = 1000*1000):boolean; begin result := false; assignfile(fIn,FInName); {$I-} reset(fIn); IF Ioresult <> 0 then rewrite(fIn); {$I+} AssignFile(fOut,FOutName); rewrite(fOut); setlength(InBuf,1 shl 15); SetTextBuf(fIn,InBuf[0],length(InBuf)); setlength(OutBuf,1 shl 15); SetTextBuf(fOut,OutBuf[0],length(OutBuf)); IF MinRowCnt>0 then GenerateDummyFile(MinRowCnt); result := true; end;
procedure GetDelimPos(const S: tMyString;var DelimPos:tDelimPos); var {$IFDEF WideChr } pC : pWChar; {$ELSE} pC : pChar; {$ENDIF} Aktpos,i : integer; begin fillchar(DelimPos,SizeOf(DelimPos),#0); IF s = '' then EXIT; pC := @S[1]; dec(pC); Aktpos := 1; DelimPos[0] := 0; i := 1; repeat if pC[Aktpos] = delimiterChar then begin DelimPos[i]:=AktPos; inc(i); end; inc(Aktpos); until Aktpos > length(S); DelimPos[i] := Aktpos; end;
function GetDelimStr(var S:string;von,bis:integer):tmyString; begin result := copy(S,von,Bis-von); end;
Procedure AppendAusgabe( s: tMyString); begin If hdAusgabe then writeln(fOut,s); end;
procedure SchreibeArray2(var name,wert:TMyString); begin AppendAusgabe(FrontStr+name+MiddleStr+wert); end;
procedure SchreibeArrs2(i: integer); begin with cConvArr[i] do begin SchreibeArray2(cvNewLogi,WertLogi); SchreibeArray2(cvNewVewa,WertVeWa); end; end;
procedure CalcConv2(Cnv:tConvert); var dNetto, dWert, dAufschlag, dLogistik, dVerwaltung : Double;
begin with Cnv do begin dWert := StrToFloat(WertStr); dAufschlag := dWert * cvFakAufschlag; dNetto := dWert - dAufschlag; dLogistik := dNetto * cvFakLogi; dVerwaltung := dAufschlag - dLogistik; dWert := dNetto + dLogistik; WertLogi := MyFloatToStr4(-dLogistik); WertVeWa := MyFloatToStr4(-dVerwaltung); end; end;
function CheckVergleich2(var s:tMyString): integer; var i : integer; begin result := 0; For i := Low(tCntVgStrings) to High(tCntVgStrings) do begin IF s = cConvArr[i].cvVgs then begin CalcConv2(cConvArr[i]); result := i; Break; end; end; end;
procedure GetStrings(var s: tMyString); begin FrontStr := GetDelimStr(s,DelimPos[0],DelimPos[4]); Checkstr := GetDelimStr(s,DelimPos[4]+1,DelimPos[5]); MiddleStr:=GetDelimStr(s,DelimPos[5],DelimPos[9]+1); WertStr:=GetDelimStr(s,DelimPos[9]+1,DelimPos[10]); end;
procedure Button2Click(rowcnt:integer); var s : tMyString; i: integer; T1,T0 : TDateTime; begin T0:= time; IF NOT(OpenIOFiles(EingabeFile,AusgabeFile,rowcnt)) then Begin writeln('Das war wohl nichts mit dem File oeffnen'); EXIT; end; T1:= time; writeln('Zeit neue Datei erstellen ',FormatDateTime('NN:SS.ZZZ',T1-T0)); rowcnt := 0; while not eof(fIn) do begin readln(fIn,s); GetDelimPos(s,Delimpos); GetStrings(s); AppendAusgabe(s); i:= CheckVergleich2(Checkstr); IF i <> 0 then SchreibeArrs2(i); inc(rowcnt); end; closefile(fIn); flush(fOut); closeFile(fOut); setlength(OutBuf,0); setlength(InBuf,0);
writeln('Bearbeitung von ',rowcnt,' Zeilen abgeschlossen'); end;
var t1,t0: TDateTime; Begin DecimalSeparator :=decSep; InitNachkomma4; hdAusgabe := true; T0 := time; Button2Click(10*1000*1000); T1 := time; Writeln(FormatDateTime('HH:NN:SS.ZZZ',T1-T0)); readln; END. |
Gruß Horst
Zuletzt bearbeitet von Horst_H am Di 08.07.14 23:23, insgesamt 2-mal bearbeitet
|
|
jaenicke
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 07.07.14 20:44
Hast du auch die Dateien, mit denen du getestet hast?
|
|
Horst_H
Beiträge: 1653
Erhaltene Danke: 243
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Di 08.07.14 06:30
Hallo,
Aus Ermangelung an Daten:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| |
Zudem werden in Ronalds Unit.pas gänzlich andere Werte benutzt ( siehe in cConvArr bei mir )auert es 50 Sekunden.
Mal als Bild:
Erstes Einlesen-> Einlesen aus Cache-> Einlesen und Verarbeiten-> Einlesen,verarbeiten und wegschreiben
Man sieht readln mit passendem Textbuf ist recht flott.
Gruß Horst
Delphi Projekt (zip, 4.21 KB)
Einloggen, um Attachments anzusehen!
|
|
|