| Autor |
Beitrag |
rushifell
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Mi 16.02.11 07:02
Hallo,
ich möchte in einem MemoryStream an einer beliebigen Position Daten entfernen bzw. einfügen. Nun habe ich dafür folgende Funktion geschrieben:
Vorsicht: Beim Einfügen, müssen die entsprechenden Daten anschließend in den Stream geschrieben werden.
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:
| Function MStream_InsertData(MStream:TMemoryStream; Offset:Integer; Resize:Integer):Boolean; Var NewPos, ActPos, Numread, BufferSize:Integer; Buffer:Array[0..4096] of Byte; Begin Result:=False; IF Assigned(MStream) then BEGIN Result:=True; MStream.Seek(Offset,soFromBeginning);
IF Resize<0 then BEGIN ActPos:=MStream.Position; NewPos:=ActPos-Resize;
Repeat MStream.Seek(NewPos,soFromBeginning); Numread:=MStream.Read(Buffer,Sizeof(Buffer)); MStream.Seek(ActPos,soFromBeginning); MStream.Write(Buffer,Numread);
ActPos:=ActPos+Numread; NewPos:=NewPos+Numread; Until Numread<Sizeof(Buffer);
MStream.SetSize(MStream.Size+Resize); END ELSE IF Resize>0 then BEGIN ActPos:=MStream.Size; NewPos:=ActPos+Resize; MStream.SetSize(NewPos); Resize:=MStream.Size-Resize-Offset;
Repeat IF SizeOf(Buffer)<Resize then BufferSize:=SizeOf(Buffer) else BufferSize:=Resize;
Resize:=Resize-BufferSize; ActPos:=ActPos-BufferSize; NewPos:=NewPos-BufferSize; MStream.Seek(Actpos,soFromBeginning); MStream.Read(Buffer,BufferSize); MStream.Seek(NewPos,soFromBeginning); MStream.Write(Buffer,BufferSize); Until Resize=0;
END; END; End; |
Da ich stets kleine Datenmengen bis zu maximal 50 kb verwende, funktioniert das recht gut, bei Daten im Megabyte-Bereich, wird das ganze doch recht langsam.
Ist es vielleicht doch sinnvoller, einen temporären Memorystream zu verwenden, die Daten bis zum Offset in diesen temporären Stream zu kopieren, die neue Daten einzufügen, dann am Offset weiterzukopieren und anschließend dem ursprünglichen Memorystream den temporären Stream zuzuweisen?
Was denkt ihr darüber? Oder gibt es noch andere Möglichkeiten?
Gruß
rushifell Moderiert von Narses: Topic aus Sonstiges (Delphi) verschoben am Mi 16.02.2011 um 09:37
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 16.02.11 08:12
Dass das langsam wird, liegt unter anderem an deiner Arrayvariablen (und an deiner Buffergröße vermutlich auch). Denn die liegt komplett auf dem Stack und da sind 4k schon extrem...
Nimm lieber einen Pointer und reserviere dir mit GetMem (+ FreeMem) direkt Speicher.
Wie es am performantesten geht, hängt auch davon ab was du mit den Daten eigentlich machst. (In Datei speichern, darauf zugreifen, ...)
Vielleicht gibt es insgesamt noch einen besseren Weg. 
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mi 16.02.11 14:45
Hallo,
ich habe mal 100000 Strings der Länge 12 eingefügt:
Quelltext 1: 2: 3: 4: 5: 6:
| Einfügen davor neue Version 00.00.09.223 Einfügen davor orig Version 00.00.16.637 Einfügen dahinter neue Version 00.00.00.044 Einfügen dahinter orig Version 00.00.00.022 Anzahl Einfügungen : 100000 Verschobene Mb : 59999 |
Es liegt also an dem ständigen davor einfügen. Es werden 60 GB in 9.22 sec verschoben.
Das ist nicht wenig.
Wie so oft, wenn man vorher weiß, wieviel Platz man baucht und an welchen Positoinen, nachher was hin muss , dann sollte man tunlichst vorher dafür den Platz schaffen.
Das 100000-fache verlängern des Streams machte überhaupt keine Probleme, da wird selten kopiert.
Resize mit negativen Zahlen habe ich jetzt nicht getestet.
Meine Variantemit Move innerhalb des memorystreams
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:
| type tbArr = array[0..0] of char; tpbArr = ^tbArr;
Function MStream_InsertData2(var MStream:TMemoryStream; Offset:Int64; Resize:Int64):Boolean; Var movelength : Int64; pm : tpbarr; Begin IF resize = 0 then begin result := true; EXIT; end;
Result:=False; IF Offset < 0 then EXIT;
IF (Offset + Resize) < 0 then begin MStream.SetSize(0); result := true; EXIT; end;
IF Assigned(MStream) then BEGIN Result:=True; movelength := (ms.Size-1)-Offset; IF Resize<0 then BEGIN pm := MStream.Memory; IF Movelength >0 then move(pm^[Offset],pm^[Offset+resize],movelength); MStream.SetSize(MStream.Size+Resize); END ELSE BEGIN MStream.SetSize(MStream.Size+Resize); pm := MStream.Memory; IF Movelength >0 then ; move(pm^[Offset],pm^[Offset+resize],movelength); END;
END; End;
procedure IncString(var s: string); var i : integer; begin i := length(s)-2; while (i > 0) do begin inc(s[i]); IF s[i] <= '9' then exit else s[i] := '0'; dec(i); end; end;
procedure TForm2.Button1Click(Sender: TObject); const Warte = 100; count = 100000; BoolAusgabePos : array[boolean] of string[9] = ('davor ','dahinter '); BoolAusgabeVer : array[boolean] of string[12] = ('neue Version','orig Version'); var T0 :TDateTime; l,ofs : Int64; memotext : string; i : integer; dahinter,testvar : boolean; begin memo1.Font.Name := 'lucida Console'; memo1.Font.Color := clWindowText; Memo1.Lines.clear;
ms:= TMemorystream.create; try For dahinter := false to true do For Testvar :=false to true do begin memotext := '0000000000'+#13#10; l := length(memotext); ms.SetSize(0); ofs := 0; t0 := time; For i := 1 to count do begin IF dahinter then begin IF testvar then MStream_InsertData(ms,Ofs,l) else MStream_InsertData2(ms,Ofs,l); ms.position:= Ofs; end else begin IF testvar then MStream_InsertData(ms,0,l) else MStream_InsertData2(ms,0,l); ms.position:= Ofs; end; incString(memotext); ms.Write(memotext[1],l); inc(Ofs,l); end; t0 := time-t0; memo1.Lines.Add(' Einfügen '+BoolAusgabePos[dahinter]+ BoolAusgabeVer[TestVar] +FormatDateTime(' hh.nn.ss.zzz',t0)); memo1.update; end; memo1.Lines.Add(' Anzahl Einfügungen : '+IntToStr(count)); memo1.Lines.Add(' Verschobene Mb : '+IntToStr( (l *( (Int64(count)*(count-1)) shr 1) div 1000000 )));
finally FreeAndNil(ms); end; end; |
Gruß Horst
Für diesen Beitrag haben gedankt: rushifell
|
|
rushifell 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Mi 16.02.11 20:08
Danke für Eure Antworten!
Ich möchte für ein Spiel "aufgezeichnete" Demos in einer Datei speichern. Für jeden Level kann man eine Demo aufzeichnen, speichern und natürlich anschauen. Also oft gespeichert wird in der Regel nicht. Die Demo-Daten werden von mir vor dem Speichern gepackt und sind in diesem Zustand im Durchschnitt etwa 300 Bytes groß (plus, minus natürlich). Ich erwarte eine Dateigröße von 10-20 kb. Da die Daten in der Level-Reihenfolge gespeichert werden, muss ich eben "einfügen" oder "entfernen.
Da stets auf die Demos und deren Status zugriffen wird, habe ich statt einem FileStream einen MemoryStream gewählt.
Ich habe mir schon überlegt, ob ich eine fixe Größe für die Daten (z.B. 500 Bytes) festlege, eine variable Größe wäre mir jedoch lieber.
Ich habe übrigens einen großen Fehler beim Testen gemacht. Das Erstellen des Teststreams hat so lange gedauert. Das Einfügen an sich dauert bei Dateien um die 10 Megabyte zwar etwas, ist aber immer noch weniger als 1 Sekunde.
@jaenicke: wie groß würdest Du den Buffer wählen? In der Regel nehme ich 1 bis 2 kb, ich dachte nur, dass es bei einem größeren Buffer etwas schneller ist.
@Horst:
Danke für Deine Variante. Die ist im Durchschnitt schneller und natürlich eleganter. In Zeile 31 ist ein kleiner Fehler. Hier die Korrektur:
Delphi-Quelltext 1:
| movelength := (ms.Size)-Offset; |
Außerdem gehört der Strichpunkt in Zeile 42 nach dem then nicht dort hin.
Vielen Dank!
Gruß
|
|
|