Autor Beitrag
zehgeh
Hält's aus hier
Beiträge: 7

Windows 2000 SP4+
Delphi 5 Prof.
BeitragVerfasst: Do 22.09.05 01:40 
Moin, moin.

Situation: Es sollen Strings in einen Memorystream kopiert werden, ohne Write/WriteBuffer zu verwenden. Das mache ich über Pointer, da es um ein vielfaches schneller geht.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
var
  p, q: PChar;  / p zeigt auf aktuelle, q auf letzte Position im Stream
  s: String;
  Buffer: TMemoryStream;

  p := Buffer.Memory;
  q = p;

  Move(Pointer(s), p^, Length(s));
  inc(p, Length(s));


Das klappt auch wunderbar. Nun geht es aber um den Speicherbedarf. Mir ist nicht bekannt, wieviel der gemeine User später einmal abzuspeichern hat, darum dachte ich daran, die Anpassung der Streamgröße dynamisch zu gestalten:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  // falls nur noch 10k übrig im Stream => vergrößern
  if q - p < 10192 then
  begin
    Buffer.SetSize(Buffer.Size + 1024 * 512);  // und zwar um 512k
    q := Buffer.Memory;
    inc(q, Buffer.Size)  // und Ende-Zeiger anpassen
  end;


Hmm, vorhin habe ich immer Schreib/Lesefehler gehabt, jetzt heißt es nun 'Out of memory' (Kann aber nicht sein). Was meint Ihr, das sollte doch eigentlich funktionieren?

Viele Grüße und danke für das Anschauen,
Christian
Heiko
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Do 22.09.05 07:32 
Hast du mal übrprüft ob er wirklich etwas schreibt? Und kannst du vlt. den gesamten Quelltext der Prozedur geben, also inkl. der Schleife die du hier ausgespart hast? Was mich wundert ist die Zeile q = p;, denn = nimmt man ja eigentlich nur zum Vergleichen. Ich vermute mal du hast den : vergessen ;)
zehgeh Threadstarter
Hält's aus hier
Beiträge: 7

Windows 2000 SP4+
Delphi 5 Prof.
BeitragVerfasst: Do 22.09.05 08:17 
Hallo.

In der Tat hatte ich einen kleinen Fehler drinnen, der sich im richtigen Programm aber nicht auswirkte - glaube ich zumindest. Hier mal das gleiche, nur gekürzt. Ein Button und zwei Memos notwendig (Memo1 -> Geladene Datei, Memo2 -> Ausgabe). Es wird eine Textdatei eingelesen - siehe erste Zeile Quelltext), anschließend werden Return (#13#10) nach #10 konvertiert.
Der Fehler tritt immer noch auf, wenn der Speicherbedarf des Streams erhöht werden soll. Und das wird er gleich am Anfang, da nach der Initialisierung die Größe auf Null feststeht.

Achja, die Daten werden korrekt geändert und geschrieben - wenn ich die Größe *vorher* festsetze, ansonsten kommt halt der Fehler. Und das verstehe ich nicht. Denn die Größe festsetzen tue ich jetzt auch zuerst, nur halt innerhalb der Schleife.

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:
procedure TForm1.Button1Click(Sender: TObject);
var
  Buffer: TMemoryStream;
  p, q  : PChar;
  i     : Word;
  s     : String;
begin
  // Dummy Strings laden
  Memo1.Lines.LoadFromFile('C:\boot.ini');
  Memo1.Lines.Text := Trim(Memo1.Lines.Text);
  if Memo1.Lines.Count = 0 then Exit;

  Buffer := TMemoryStream.Create;
  try
    with Memo1 do
    begin
      p := Buffer.Memory;
      q := p;
      for i := 0 to Lines.Count - 1 do
      begin
        // falls nur noch 10k übrig im Stream => vergrößern
        if q - p < 10 * 1024 then
        begin
          Buffer.SetSize(Buffer.Size + 1024 * 512);  // und zwar um 512k
          q := Buffer.Memory;
          inc(q, Buffer.Size)
        end;
        // String holen...
        s := Lines[i];

        //...und in den MemoryStream kopieren
        Move(s[1], p^, Length(s));
        // statt vorher
        //Move(Pointer(s), p^, Length(s));
        inc(p, Length(s));

        // Ersetze das Return (#13#10)durch #10
        p^ := #10;
        inc(p);
      end  {of i := 0 to Lines.Count - 1}
    end;  {of with Memo1 do}
    Buffer.SetSize(p - Buffer.Memory);
    p := Buffer.Memory;
    Memo2.Lines.SetText(p)
  finally
  Buffer.Free
  end
end;


Vielen Dank für Eure Blicke.
Christian
wdbee
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Do 22.09.05 09:44 
Delphi-Hilfe:
Zitat:

Die Methode SetSize setzt die Eigenschaft Size des Speicher-Streams.

procedure SetSize(NewSize: Longint); override;

Beschreibung

Mit SetSize legt man die Größe eines Speicher-Streams fest, bevor dieser mit Daten gefüllt wird. Maßgebend ist der Wert des Parameters NewSize. Vor der Reservierung wird der aktuelle Inhalt von Memory verworfen. Der Speicherpuffer muß somit nicht freigegeben werden.

SetSize wird aufgerufen, bevor der Speicher-Stream mit Daten aus verschiedenen Quellen oder mit dem Teilinhalt eines anderen Streams gefüllt wird. Wenn der vorgesehene Inhalt des Streams exakt mit dem Inhalt eines anderen Streams oder einer Datei übereinstimmt, verwenden Sie statt dessen die Methoden LoadFromStream oder LoadFromFile.

Das heißt, dass nicht sichergestellt werden kann, dass der bisherige Inhalt erhalten bleibt, wenn SetSize aufgerufen wird! Inkrementelles Anpassen geht deshalb nicht!

Wenn der Speicher durch Zufall am Ende des bisher genutzten Blocks liegt und in diesem Block genug Speicher ist, um den neuen Bereich aufzunehmen, dann funktioniert das sogar, denn dann beginnt der Speicher an der alten Adresse und hat eine größere Kapazität. Das kann aber nicht vorausgesetzt werden!!!
zehgeh Threadstarter
Hält's aus hier
Beiträge: 7

Windows 2000 SP4+
Delphi 5 Prof.
BeitragVerfasst: Do 22.09.05 10:51 
Hallo.

Delphi Hilfe zu TMemoryStream hat folgendes geschrieben:

Use SetSize to set the Size of a memory stream before filling it with data. SetSize allocates the memory buffer to hold NewSize bytes, preserving as much of the existing data as possible.


Wenn ich den Bereich also vergrößere, bleibt das davor erhalten. Nichtsdestotrotz habe ich ja das Problem, dass ich den Speicher ja auch gleich am Anfang der Schleife zuweise...

Christian
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Do 22.09.05 10:52 
Hi ZehGeh,

vielleicht kannst Du uns(mir) mal erklären, was Du damit bezweckst. Denn ich verstehe den Sinn von dieser Zweckentfremdung eines Streams nicht.

Danke

_________________
Na denn, dann. Bis dann, denn.
zehgeh Threadstarter
Hält's aus hier
Beiträge: 7

Windows 2000 SP4+
Delphi 5 Prof.
BeitragVerfasst: Do 22.09.05 11:01 
Hi.

Es geht lediglich darum, für mich interessantes Zeugs aus Textdateien zu extrahieren und dann neu abzuspeichern. Es werden Zeichenketten und Zahlen benötigt, wobei mit letzteren im Programm noch gearbeitet wird. Schlußendlich ein Log-Analysierungsprogramm. Ich hatte das schon mal auf Basis einer TStringList, aber das war Speicherintensiv und langsam. Mit dem MemoryStream bin ich sehr zufrieden, bis auf das angesprochene Problem. Muss mal schauen, ob ich mit GetMem weiterkomme. Unter DOS (lang ist's her), hatte ich mal eine Assembler Funktion zum Vergrößern des Speicherbereichs, ohne überschreiben. Das sollte heute eigentlich auch noch gehen.

Christian
wdbee
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Do 22.09.05 11:14 
user profile iconzehgeh hat folgendes geschrieben:
Hallo.

Delphi Hilfe zu TMemoryStream hat folgendes geschrieben:

... preserving as much of the existing data as possible.



Das heißt für mich nur: Wenn ich den Bereich vergrößere, bleibt der bisherige Inhalt manchmal erhalten, manchmal aber auch nicht.
zehgeh Threadstarter
Hält's aus hier
Beiträge: 7

Windows 2000 SP4+
Delphi 5 Prof.
BeitragVerfasst: Do 22.09.05 11:27 
Und logisch heißt es, dass der Bereich lediglich überschrieben wird, wenn der Bereich *verkleinert* wird. Meine Logik voruasgesetzt. Aber mal abgesehen vom Inhalt des Speicherbereiches: Wenn ich die Speicherreservierung aus der Schleife entferne und stattdessen einen überdimensionierten Speicherblock vorher reserviere, funktioniert alles. Wenn ich aber innerhalb der Schleife Speicher reserviere - ich greife noch *nicht* darauf zu - kommt es zum Fehler. Und durch den Vergleich q - p < sonstwas muss sofort nach dem Start in die Speicherreservierung gesprungen werden - das klappt auch. Ich habe es bisher nicht mit den Originaldatenmengen getestet, die inkrementelle Erhöhung wird also nur einmal, nämlich bei Start, ausgeführt.

Christian
wdbee
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Do 22.09.05 11:40 
Überschrieben wird nichts, der alte Inhalt bleibt als Leiche dort. Erst wenn der Speicher neu zugewiesen wird, ändert sich der Inhalt ggf. Deshalb ist das nicht sofort zu erkennen, wenn man mit Zeigern arbeitet und debuggt.

Wenn p dein Zeiger auf den Beginn des Speicherblockes ist und q der auf den Ende dieses Blocks dann ist nach deiner Logik q - p die Länge des Speicherblocks. Wenn nun ein größerer Speicher benötigt wird kann es passieren, dass ab der Stelle, auf die p zeigt, nicht mehr genug Platz ist, weil nach dem Speicherblock schon andere genutzte Speicherblöcke liegen. Dann wird der neue Block an anderer Stelle zugewiesen. Aber dass heißt dann doch, dass p nicht (mehr) auf den Anfang des (neuen) Speicherblocks zeigt. Wenn der neue Block vor dem alten liegt, wäre q - p negativ und wenn er danach liegt zu groß!

Wenn du zuerst SetSize aufrufst und dann p zuweist passt das. Wenn du erst p zuweist und dann SetSize nicht. Denke ich da richtig?
sahib
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 117

Win 2000 SP4+
Delphi 5 Prof.
BeitragVerfasst: Do 22.09.05 11:48 
Danke, danke!

Stimmt, ich habe nicht bedacht, dass der Speicher nicht aneinander liegen muss. Das müsste es sein. Kann ich ja später testen.

*EDIT* Ok, wo ist der Knopf 'Als gelöst' hin? Ich habe hier soetwas nicht und die Buttons 'Style', 'Bereiche' etc funktionieren auch nicht.


Christian
(wieder mit altem Pseudonym)