Autor Beitrag
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Di 10.01.12 13:07 
Hallo zusammen!

Ich habe mir ein kleines Tool geschrieben um die Geschwindigkeit von WriteLn bei einem TextFile mit TFileStream zu vergleichen.

Herausgekommen sind bei mir große Unterschiede, daher vermute ich den Fehler in meiner Implementierung:
ausblenden volle Höhe Delphi-Quelltext
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:
program Test;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  Classes,
  Math;
const
  cCount = 10000000// 10.000.000
  cTestAnz = 20;

var
  Test: Integer;
  MinZeit, MaxZeit, ZeitSum, Zeit, Freq: Int64;
  I: Integer;
  Anz: Integer;
  
function WriteLnTest: Int64;
var
  Start, Ende: Int64;
  F: TextFile;
  I: Integer;
  Text: string;
begin
  QueryPerformanceCounter(Start);
  AssignFile(F, 'C:\Test.dat');
  try
    Rewrite(F);

    for I := 1 to cCount do
    begin
      Text := Format('Wert: %d', [I]);
      WriteLn(F, Text);
      if (I mod 100000) = 0 then
        Write(#13 + Format('Test %2d: ', [Test]) +  FormatFloat('000%', (I / cCount) * 100));
    end;
  finally
    CloseFile(F);
  end;
  QueryPerformanceCounter(Ende);

  Result := (Ende - Start);
end;

function StreamWriteTest: Int64;
var
  Start, Ende: Int64;
  I: Integer;
  Stream: TFileStream;
  Text: string;
begin
  QueryPerformanceCounter(Start);

  Stream := TFileStream.Create('C:\TestSt.dat', fmCreate or fmShareExclusive);
  try
    for I := 1 to cCount do
    begin
      Text := Format('Wert: %d', [I]) + #13#10;
      Stream.WriteBuffer(Text[1], Length(Text));
      if (I mod 100000) = 0 then
        Write(#13 + Format('Test %2d: ', [Test]) +  FormatFloat('000%', (I / cCount) * 100));
    end;
  finally
    Stream.Free;
  end;
  QueryPerformanceCounter(Ende);

  Result := (Ende - Start);
end;

begin
  try
    ZeitSum := 0;
    MinZeit := MaxLongint;
    MaxZeit := 0;
    Anz := 0;

    QueryPerformanceFrequency(Freq);

    WriteLn('WriteLn-Test');
    for I := 1 to cTestAnz do
    begin
      Test := I;
      Zeit := WriteLnTest;
      ZeitSum := ZeitSum + Zeit;
      MinZeit := Min(MinZeit, Zeit);
      MaxZeit := Max(MaxZeit, Zeit);

      WriteLn(#13 + Format('Test %2d: 100%%; Zeit: %g', [Test, Zeit/Freq]));
      Inc(Anz);
    end;
    WriteLn('----------------------------------------------');
    WriteLn(Format('| Min  : %g', [MinZeit/Freq]));
    WriteLn(Format('| Max  : %g', [MaxZeit/Freq]));
    WriteLn(Format('| Avg  : %g', [(ZeitSum/Anz)/Freq]));
    WriteLn('----------------------------------------------');
    WriteLn;

    ZeitSum := 0;
    MinZeit := MaxLongint;
    MaxZeit := 0;
    Anz := 0;

    QueryPerformanceFrequency(Freq);

    WriteLn('StreamWrite-Test');
    for I := 1 to cTestAnz do
    begin
      Test := I;
      Zeit := StreamWriteTest;
      ZeitSum := ZeitSum + Zeit;
      MinZeit := Min(MinZeit, Zeit);
      MaxZeit := Max(MaxZeit, Zeit);

      WriteLn(#13 + Format('Test %2d: 100%%; Zeit: %g', [Test, Zeit/Freq]));
      Inc(Anz);
    end;
    WriteLn('----------------------------------------------');
    WriteLn(Format('| Min  : %g', [MinZeit/Freq]));
    WriteLn(Format('| Max  : %g', [MaxZeit/Freq]));
    WriteLn(Format('| Avg  : %g', [(ZeitSum/Anz)/Freq]));
    WriteLn('----------------------------------------------');
    WriteLn;
    WriteLn('Fertig');
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
  ReadLn;
end;


Hier das Ergebnis:
Geschwindigkeit

Die Unterschiede fallen ziemlich groß aus (15 zu 60 Sekunden), daher vermute ich einen Fehler in meiner Implementierung - oder sind die Unterschiede wirklich so groß?

Edit:

Ich habe meinen Test jetzt umgebaut, so dass ich erst in einen TMemoryStream schreibe und diesen dann per CopyFrom in einen TFileStream speichere. Das dauert dann sogar nur noch um die 10 Sekunden (3 Sekunden das Füllen des TMemoryStream, 7 Sekunden das CopyFrom).

Aber die Ursprungsfrage bleibt:
Warum ist das WriteBuffer des TFileStream soviel langsamer als WriteLn?
Einloggen, um Attachments anzusehen!
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Di 10.01.12 20:02 
Kurz, hab keine Zeit:

Buffering. Dein Stream buffert gar nicht, TextFile hat 128byte und mit MemoryStream vorher... naja, den ganzen Stream eben.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
baka0815 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Mi 11.01.12 11:05 
Also stimmt meine Vermutung, dass das Write des TFileStream direkt auf die Festplatte schreibt (Flush) und nicht selbst puffert - wieso?

Gibt es in Delphi (wie z.B. in Java) einen BufferedStream, der mir die Pufferung bereits abnimmt oder muss ich mir da komplett selbst was bauen (ich nutze aktuell Delphi 2007).

Wo ist dann der Unterschied zwischen den Funktionen Write und WriteBuffer des TFileStream?
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mi 11.01.12 12:28 
user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
Also stimmt meine Vermutung, dass das Write des TFileStream direkt auf die Festplatte schreibt (Flush) und nicht selbst puffert - wieso?
Geflusht wird nicht direkt, aber auch wenn Windows das selbst nochmal in seinen 64k-Schnipseln buffert ist das vergleichsweise langsam. Vermutlich wegen dem Verwaltungsaufwand - ein WriteFile kostet so um die 5us, besonders wenn man wenig schreibt. Wird nach oben hin natürlich mehr, aber die "Fixkosten" bleiben. Sieht man auch im ProcessMonitor schön.

user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
Gibt es in Delphi (wie z.B. in Java) einen BufferedStream, der mir die Pufferung bereits abnimmt oder muss ich mir da komplett selbst was bauen (ich nutze aktuell Delphi 2007).
Bis BDS2006 gabs noch keinen. Sowas wollte ich immer mal bauen (und hab auch grade für MUO einen BufferedReadStream geschrieben), aber was komplett immer funktionierendes ist nicht so ganz einfach wie ich feststellen musste ;)
TReader und TWriter Buffern allerdings, vielleicht ist das ja verwendbar.

Das komplette Streamingsystem gehört neugeschrieben, aber das systemweit umzupatchen (immerhin wird TFileStream überall verwendet) ist nicht so ganz einfach. Müsste man mal AndyH fragen. Und, was viel wichtiger ist: müsste man mal Zeit haben :?


user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
Wo ist dann der Unterschied zwischen den Funktionen Write und WriteBuffer des TFileStream?
Kein Nennenswerter, nur die Datentypen sind etwas anders.
EDIT: nicht mal das, der Unterschied nur dass *Buffer eine Fehlerprüfung macht und ggf. eine Exception auslöst.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 11.01.12 12:46 
user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
Gibt es in Delphi (wie z.B. in Java) einen BufferedStream, der mir die Pufferung bereits abnimmt oder muss ich mir da komplett selbst was bauen (ich nutze aktuell Delphi 2007).
Ich habe die Art und Weise des Schreibens zwar nicht mehr im Kopf, aber vielleicht ist das ja etwas für dich:
[url]www.delphi-forum.de/...c.php?t=100088[/url]
(Und wenn nicht, sollte zumindest darauf aufzubauen sein. ;-))
baka0815 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Do 12.01.12 16:37 
Danke dir für den Tipp, allerdings wäre es schön einen generischen TBufferedStream o.ä. zu haben, den man einfach über einen bestehenden TStream überstülpen kann, damit das nicht nur für Dateizugriff funktioniert.

Für diesen Beitrag haben gedankt: BenBE, Martok
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mo 16.01.12 22:17 
Also wenn es wirklich schnell sein sollte, nimmt man eh MMFs. Dann ist das Limit mehr oder weniger die Geschwindigkeit der Festplatte.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.