Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt? (https://www.delphipraxis.net/191345-tfilestream-write-langsam-wenn-man-nur-1-zeichen-schreibt.html)

a.def 4. Jan 2017 18:36

TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Ich schlage mich gerade mit einem TFileStream herum und merke, dass der doch recht langsam ist, wenn man
(Beispiel) 0 bis X-Tausend-Mal nur ein Zeichen schreibt.

Ich möchte gerne X-Mal ein Zeichen in eine Datei schreiben. X kann hier 0 bis mehrere Tausend Mal sein.
Ich dachte erst, ich verwende den Buffer. Aber bei einem Zeichen pro Aufruf ist das ja total sinnlos.

Hätte hier jemand eine bessere Idee?

Delphi-Quellcode:
aFileStream := TFileStream.Create('C:\test.txt', fmCreate or fmOpenWrite);

try
 for j := 0 to 14999 do
  begin
   sTmp := 'X';
   // aFileStream.WriteBuffer(Pointer(sTmp)^, Length(sTmp));
   aFileStream.Write(PChar(sTmp)^, Length(sTmp));
 end;
finally
 aFileStream.Free;
end;
Meine Idee wäre, jeweils XYZ Zeichen zu sammeln, in einen String schreiben und diesen dann mit dem FileStream schreiben. Aber ist das sinnvoll?

Daniel 4. Jan 2017 18:40

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Kilobytes oder auch eine Handvoll Megabytes kannst Du im Speicher vorbereiten und dann in einem Rutsch auf die Platte schreiben.
Im einfachsten Fall ein Array of Bytes btw. TArray<Byte> oder meinetwegen ein MemoryStream.
Alternativ gäbe es auch den "System.Classes.TBufferedFileStream", der genau dazu da ist, nacheinander viele kleine Dinge wegzuschreiben.

a.def 4. Jan 2017 18:56

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Danke für die Idee! Geht schon deutlich schneller.

Mein "Kot"
Delphi-Quellcode:
       iFileSizeToWrite := 15728640; // 15 MB
       aFileStream := TFileStream.Create('C:\test.txt', fmCreate or fmOpenWrite);

       try
        iBytesCounter := 0;
        repeat
         sTmp := '';
         for j := 1 to (1024 * 1024) do // 1 MB pro Durchgang
          begin
           sTmp := sTmp + 'X';
           Dec(iFileSizeToWrite);
           if iFileSizeToWrite = 0 then
            Break;
          end;

         aFileStream.Write(PChar(sTmp)^, Length(sTmp));
        until iFileSizeToWrite = 0;
       finally
        aFileStream.Free;
       end;
Ob das noch schneller ginge?

EWeiss 4. Jan 2017 18:57

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Mein "Kot"
OT:

Macht der irgendwo hin?
Zumindest sehe ich keine Fäkalien.

gruss

Daniel 4. Jan 2017 19:01

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Zitat von a.def (Beitrag 1358096)
Ob das noch schneller ginge?

Bist Du sicher, dass ein String das rechte Werkzeug zur Datenhaltung ist? Du baust den ja Zeichen für Zeichen auf. Das arme Ding wird im Speicher kreuz und quer kopiert. Wenn es unbedingt ein String bleiben soll, dann setze dessen Länge einmalig am Anfang und setze die einzelnen Zeichen dann über deren Index (ala s[1] := 'X'; ).

a.def 4. Jan 2017 19:07

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Zitat von EWeiss (Beitrag 1358097)
Zitat:

Mein "Kot"
OT:

Macht der irgendwo hin?
Zumindest sehe ich keine Fäkalien.

gruss

Ich dachte ich nenne es "Kot", da es definitiv keiner guter Code ist :thumb:


Zitat:

Zitat von Daniel (Beitrag 1358098)
Zitat:

Zitat von a.def (Beitrag 1358096)
Ob das noch schneller ginge?

Bist Du sicher, dass ein String das rechte Werkzeug zur Datenhaltung ist? Du baust den ja Zeichen für Zeichen auf. Das arme Ding wird im Speicher kreuz und quer kopiert. Wenn es unbedingt ein String bleiben soll, dann setze dessen Länge einmalig am Anfang und setze die einzelnen Zeichen dann über deren Index (ala s[1] := 'X'; ).

Ok ein kleiner Test. Mit meinem... Code... da oben: 512MB in ~7,3 Sekunden. Mit einem array of string ~17 Sekunden.
Mit sTmp: string; SetLength(sTmp, XYZ); sTmp[1] := 'X'; bekomme ich leider eine AV.

EWeiss 4. Jan 2017 19:09

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Ich dachte ich nenne es "Kot", da es definitiv keiner guter Code ist
Tja dann ist es berechtigt ;)
War auch nur ein joke :)

gruss

a.def 4. Jan 2017 19:21

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Ich habe gerade ein Programm getestet, dass mehrere GB in wenigen Sekunden auf die Platte schreibt :pale:

Daniel 4. Jan 2017 19:25

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Wo genau willst Du hin?
Du möchtest offenbar ein bestimmtes Byte vielfach in eine Datei schreiben?
Über welche Größenordnung an Dateigröße reden wir konkret? Bislang war in Deinem ersten Beitrag von Kilobytes die Rede.
Über welche Anforderungen an das Zeitverhalten reden wir? Bei "mehreren GB pro Sekunde" reden wir langsam auch von Hardwarefragen.

a.def 4. Jan 2017 19:28

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Stimmt das sollte ich mal klären.
Es kann um Dateigrößen von Bytes bis GBytes gehen. Je nachdem was man angibt.
Das sollen eigentlich nur dummy-Dateien werden. Den Inhalt kann man ebenfalls bestimmen. Entweder ein zufälliges Zeichen oder ein definiertes Zeichen.

Ich bin schon so weit, dass alles korrekt arbeitet. Nur 7 Sekunden für 512 MB auf eine SSD ist ein wenig happig :pale:

P.S.: das Tool, das mehrere GB pro Sekunde schreibt, das war auf meinem PC :P

Daniel 4. Jan 2017 19:45

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Möglicherweise musst Du Dich noch mit dem Windows Dateicache auseinandersetzen. Ein und derselbe Code habe hier auf dem System zeigt höchst unterschiedliche Laufzeiten. Die 500 Mbytes benötigen zwischen 1.6 und 8 Sekunden, um ihren Weg auf die Platte zu finden.

Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.Classes,
  System.Diagnostics;

const
  ZwanzichMB = 1024 * 1024 * 20;
var
  LStream : TFileStream;
  LBytes : TBytes;
  LWatch : TStopwatch;
  i : integer;
begin
  DeleteFile('C:\Temp\wuppdi.dat');

  WriteLn('bereite Daten vor ...');
  SetLength( LBytes, ZwanzichMB );
  FillChar( LBytes[Low(LBytes)], Length(LBytes), 42 );

  WriteLn('schreibe Daten ...');
  LWatch := TStopwatch.StartNew;

  LStream := TFileStream.Create( 'C:\Temp\wuppdi.dat', fmCreate OR fmOpenWrite );
  TRY
    for i := 1 to 25 do
      LStream.Write( LBytes, Length(LBytes) );
  FINALLY
    LStream.Free;
  END;

  LWatch.Stop;
  WriteLn( 'fertig nach ', LWatch.ElapsedMilliseconds, 'msecs' );
  ReadLn;
end.

a.def 4. Jan 2017 20:19

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Also dieses Beispiel muss ich erstmal verdauen. Denn es funktioniert a) einfwandfrei und b) es ist wahnsinnig schneller.

Ist meine Anpassung, um eine bestimmte Größe schreiben zu können, so in Ordnung?
Mir persönlich gefällt die Prüfung auf <= 0 nicht, weiß aber sonst nicht wie man es machen könnte.

Delphi-Quellcode:
        iFileSizeToWrite := 1024 * 1024 * 512;
        repeat
         // aBufferSize := 1024 * 1024 * 20;

         if aBufferSize > iFileSizeToWrite then
          aBufferSize := iFileSizeToWrite;

         Dec(iFileSizeToWrite, aBufferSize);

         SetLength(aBytes, aBufferSize);
         FillChar(aBytes[Low(aBytes)], Length(aBytes), Ord('A'));
         aFileStream.Write(aBytes, Length(aBytes));
        until iFileSizeToWrite <= 0;

Der schöne Günther 4. Jan 2017 21:54

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Nimmt einem TBufferedFileStream nicht gerade diese Arbeit ab?

p80286 4. Jan 2017 22:28

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Warum füllst Du bei jedem Schreibvorgang den Buffer erneut mit 'A' ?

Sag doch mal was Du erreichen willst.

Gruß
K-H

EWeiss 4. Jan 2017 22:39

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Zitat von p80286 (Beitrag 1358112)
Warum füllst Du bei jedem Schreibvorgang den Buffer erneut mit 'A' ?

Sag doch mal was Du erreichen willst.

Gruß
K-H

Zitat:

Ich möchte gerne X-Mal ein Zeichen in eine Datei schreiben
Ich denke er will nur eine Methode erlernen ein Zeichen (X-Mal) so schnell wie möglich in eine Datei zu schreiben.

Das scheint er ja mit Hilfe von Daniel erreicht zu haben.
Ein besonderer Grund muss dafür abweichend vom genannten nicht zwingend notwendig sein.
Es steckt manchmal einfach nur der Sinn dahinter etwas lernen zu wollen.

gruss

a.def 4. Jan 2017 22:41

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Zitat von EWeiss (Beitrag 1358113)
Es steckt manchmal einfach nur der Sinn dahinter etwas lernen zu wollen. gruss

:thumb:

EWeiss 4. Jan 2017 22:50

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Zitat von a.def (Beitrag 1358114)
Zitat:

Zitat von EWeiss (Beitrag 1358113)
Es steckt manchmal einfach nur der Sinn dahinter etwas lernen zu wollen. gruss

:thumb:

Na ja ;)

Ich finde es gut wie du es machst habe einige deiner erstellten Themen verfolgt.
Du akzeptierst nicht alles was man dir vorlegt, hinterfragst und bist erst dann glücklich wenn du eine für dich
Nachvollziehbare akzeptable Lösung gefunden hast.
Dem ist nicht entgegenzusetzen.

Von daher geht das :thumb: zurück.

gruss

a.def 4. Jan 2017 22:57

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Ich stelle mich oft aber auch extrem dumm an, das muss ich dazusagen.

himitsu 4. Jan 2017 22:59

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Such hier mal meinen FileSplitter. (große Datenmengen schnell lesen/schreiben)

Es gibt grundsätzlich erstmal mehrere Wege für Dateizugriffe, die auch kombiniert werden können.
* MemoryMappedFiles > Dateiinhalt in den Arbeitsspeicher gemappt
* buffered ReadFile/WriteFile
* nonbuffered ReadFile/WriteFile
* man kann beim Dateiöffnen angeben, ob man sequentiell oder zufällig auf die Datei zugreifen will (das soll angeblich z.B. den WindowsFileCache optiomieren)
* dann kann man das Ganze auch noch synchron oder asynchron ausführen > ReadFile/WriteFile warten bis sie fertig sind oder kehren schon vorher zurück
* wenn man will kann man auch noch 'ne Transaction dazwischenschalten
* und zusätzlichen privaten Pufferspeicher im Programm, vorallem wenn man sehr oft wenige Bytes zusammenhängend hat > also erst in einem Buffer zusammenfassen und dann zusammen in die Datei schreiben, bzw. in einen Buffer auslesen und dann den Kleinkram dort rausholen (macht z.B. das alte Read/Write/ReadLn/WriteLn)

* Windows legt standardmäßig den WindowsFileCache dazwischen, also dein WriteFile landet erstmal da und dann später erst auf der Platte
beim Lesen natürlich andersrum und ein zweiter Lesezugriff ist schneller, wenn es da schon drin ist
* aber z.B. bei Wechseldatenträgern ist der Schreibcache oft deaktiviert (bzw. wird umgehend wieder geleert)
* dann kommt noch der Cache des IO-Controllers und des Datenträgers (HDD)
* die Clustergröße des Dateisystems und die größe der Speicherblöche des Datenträges spielen eine Rolle
* und natürlich auch die Größe der verschiedenen Cache und des freien Arbeitspeichers (für WFC)
* Cache- und Blockgrößen in den Datenträgern unterscheiden sich auch zwischen USBStick/MemoryCard, SSD, HybridDisk und HDD und dann natürlich auch zwischen kleinem und großen Datenträger

So, jetzt hast du millionen Systemkonfigurationen und es ist nahezu unmöglich dass es überall gleich gut läuft, aber man kann gewisse Grundeigenschaften "optimieren".

EWeiss 4. Jan 2017 23:00

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Zitat von a.def (Beitrag 1358116)
Ich stelle mich oft aber auch extrem dumm an, das muss ich dazusagen.

geht mir genauso keine Sorge.
Es hat auch nichts mit schleimen zu tun auch verdient es keinen Pokal aber zumindest meine Anerkennung
das du nicht alles so hinnimmst sondern Hinterfragst und LERNEN willst.

OK wird jetzt OT:
Damit ist alles gesagt. ;)

gruss

a.def 4. Jan 2017 23:03

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Ich dachte bis vor ein paar Stunden tatsächlich noch, dass das OK ist so wie ich meinen String im Speicher zusammengebaut habe.
Ich habe zwar schon von FillChar gehört wusste aber nicht, dass man es eben hier so gut einsetzen kann.

EWeiss 4. Jan 2017 23:08

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
FillChar ist ein mächtiges Instrument vor allem dann wenn man Strings\Und Konsorte zurücksetzen oder die Variable in dem Fall Result initialisieren will.
Delphi-Quellcode:
FillChar(Result, SizeOf(Result), 0);


gruss

himitsu 4. Jan 2017 23:26

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
FillChar ist ein "böser" Name, aber wenn man bedenkt, dass in C++ ein Byte auch als UCHAR benannt wird (unsigned char)
und dass "früher" auch im Delphi ein Char (AnsiChar) mal 1 Byte groß war.

PS: FillChar -> ZeroMemory (kennt auch Delphi)
In C++ gibt es noch SecureZeroMemory, da dort der Compiler eventuell das "Macro" MSDN-Library durchsuchenZeroMemory wegoptimiert, wenn man danach nicht mehr lesend auf diese Variable zugreift. (aber Delphi macht das nicht, also einfach den Hinweis im MSDN ignorieren :stupid:)

EWeiss 4. Jan 2017 23:28

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Wobei ich mich frage ob SetLength vor FillChar überhaupt von nöten ist denn
den Speicher könnte man direkt mit FillChar zuweisen.

Welchen Sinn macht das?

Zitat:

PS: FillChar -> ZeroMemory (kennt auch Delphi)
ZeroMemory ist doch letztendlich der WinApi32 ersatz für FillChar (Delphi).

gruss

himitsu 4. Jan 2017 23:42

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Nein, kann man nicht.
FillChar/ZeroMemory überschreibt nur bestehenden Speicher. Es wird niemals Speicher reserviert.
PS: SetLength hat schon ein FillChar integriert. (aber nur für Arrays und nicht für Strings)

In Bezug auf Strings gibt es auch noch SetString, was ein SetLength+CopyMemory ist.

Sinn, erstmal in Bezug auf CodeConvertierung zwischen Delphi und C++.
Und dann die "logische" Ausrichtung. > FillChar/MemCopy/GetMem/FreeMem, ZeroMemory/CopyMemory/GetMemory/FreeMemory usw. also wie die Frage nach Record oder Static-Class :lol:

EWeiss 4. Jan 2017 23:47

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

FillChar/ZeroMemory überschreibt nur bestehenden Speicher. Es wird niemals Speicher reserviert.
Ok dann habe ich auch noch was dazu gelernt. ;)

Bin jetzt davon ausgegangen das dies

Delphi-Quellcode:
FillChar(aBytes, SizeOf(aBufferSize), 0);


1. Die Variable aBytes initialisiert
2. die länge von aBytes mit SizeOf zuweist
3. Den zugewiesenen Speicher mit 0 füllt.

Sollte soweit stimmen bis auf das der Speicher nicht reserviert wird.

gruss

a.def 4. Jan 2017 23:49

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Zitat von EWeiss (Beitrag 1358126)
Wobei ich mich frage ob SetLength vor FillChar überhaupt von nöten ist denn
den Speicher könnte man direkt mit FillChar zuweisen.

Welchen Sinn macht das?

Zitat:

PS: FillChar -> ZeroMemory (kennt auch Delphi)
ZeroMemory ist doch letztendlich der WinApi32 ersatz für FillChar (Delphi).

gruss

Ich habe SetLength jetzt dahin verlegt, wo es gebraucht wird - da, wo die Buffer-Größe verändert wird. Das passiert A) nur am Anfang vor der Schleife und B) eventuell ganz am Ende ein Mal.

t.roller 5. Jan 2017 00:14

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Zitat:

Zitat von a.def (Beitrag 1358104)
P.S.: das Tool, das mehrere GB pro Sekunde schreibt, das war auf meinem PC :P

Wie heisst das?

Mavarik 5. Jan 2017 03:18

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Daniel (Beitrag 1358106)
Möglicherweise musst Du Dich noch mit dem Windows Dateicache auseinandersetzen. Ein und derselbe Code habe hier auf dem System zeigt höchst unterschiedliche Laufzeiten. Die 500 Mbytes benötigen zwischen 1.6 und 8 Sekunden, um ihren Weg auf die Platte zu finden.

Gut, auch eine Frage der Hardware...

Anbei ein Bleispiel - Dein Source ohne eine Änderung!

Einmal aus meiner Windows 10 VM

und

Einmal aus dem nativen Windows 10 wo die VM drauf läuft. (Bei laufenden VM)

8-) Mavarik

himitsu 5. Jan 2017 09:53

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
MSDN-Library durchsuchenFlushFileBuffers nach dem Write, dann hat man auch die Zeit bis Windows wirklich alles an den Datenträger geschickt hat.

Aus diesem Grund ist auch beim Schreiben großer Dateien der Durchsatz am Anfang größer, als zum Ende hin, nachdem der RAM überfüllt ist.
Bzw. der Explorer kopiert ja auch am Anfang schneller, außer bei Wechseldatenträgern mit deaktiviertem Schreibcache. (Stichwort "schnelles Entfernen")

t.roller 6. Jan 2017 16:33

AW: TFileStream.Write langsam, wenn man nur 1 Zeichen schreibt?
 
Andere Möglichkeit:

ms-help://embarcadero.rs_xe7/libraries/System.IOUtils.TFile.WriteAllBytes.html
Delphi-Quellcode:
procedure SaveBytesToFile3(const Data: TBytes; const FileName: string);
 begin
   TFile.WriteAllBytes( FileName, Data ); // uses System.IOUtils
 end;

procedure TForm1.Button24Click(Sender: TObject);
var Data : TBytes;
    FN : String;
    SO : Cardinal;
    Ticks: DWord; Res : Single;
begin
SO:= 1000000000; // 1GB
FN:='G:\TEST\BIGFILE1GB.txt';
  SetLength( Data, SO);
  FillChar( Data[0], SO, $41);
  Memo1.Lines.Add('Start creating '+FN);
  Ticks := timeGetTime; //uses MMSystem
SaveBytesToFile3(Data,FN);
  Res := 0.001 * (timeGetTime - Ticks);
  Memo1.Lines.Add(Format('Time for creating '+FN+' : '+' %.3f s',[Res]));
  Memo1.Lines.Add('Speed per sec: '+ floattostrf(SO / Res, ffNumber, 10, 0)+' Bytes/sec');
end;
--------------------------
USB3.0 - 240GB SSD
Start creating G:\TEST\BIGFILE1GB.txt
Time for creating G:\TEST\BIGFILE1GB.txt : 4,571 s
Speed per sec: 218.770.505 Bytes/sec

Start creating G:\TEST\BIGFILE1GB.txt
Time for creating G:\TEST\BIGFILE1GB.txt : 4,720 s
Speed per sec: 211.864.416 Bytes/sec
----------------------------------------


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz