Optimal Buffer Stream write process

三世轮回 提交于 2019-12-07 18:06:51

问题


We have our own data streaming algorithm that include some metadata+records+fields values.

Currently we use a TStream and write to add values to the stream. Now I'm wondering if this time cosuming operation could be made faster by using some technic.

Edit: We are only appending data to the end, not moving or seeking.

Some things I was thinking of are:

  • Not use Streams buf some large memory allocated buffer to copy data to, the problem is if we go beyond the buffer size, then we have to relocate to some new memory space.
  • Use a Stream prefilled with #0s to some size and then start adding values. The rationale is that Tstream have to allocate it's own buffer every time I do a write (I don't know how it really works, just wondering...)

We are adding strings to the TStream and binary data in the form #0#0#0#1 for example.

The data is then transfered via TCP, so it's not about write to a File.

So what is the best method for this?


回答1:


Overwrite TMemoryStream and remove the restriction of Size and Capacity. And do not call TMemoryStream.Clear but call TMemoryStream.SetSize(0)

type
  TMemoryStreamEx = class(TMemoryStream)
  public
    procedure SetSize(NewSize: Longint); override;
    property Capacity;
  end;

implementation

{ TMemoryStreamEx }

procedure TMemoryStreamEx.SetSize(NewSize: Integer);
var
  OldPosition: Longint;
begin
  if NewSize > Capacity then
    inherited SetSize(NewSize)
  else
  begin
    OldPosition := Position;
    SetPointer(Memory, NewSize);
    if OldPosition > NewSize then
      Seek(0, soFromEnd);
  end;
end;



回答2:


First, you are assuming the TStream is the bottleneck. You need to profile your code, such as with AQTime, to determine where the bottleneck really is. Don't make assumptions.

Second, what type of TStream are you actually using? TMemoryStream? TFileStream? Something else? Different stream types handle memory differently. TMemoryStream allocates a memory buffer and grows it by a pre-set amount of bytes whenever the buffer fills up. TFileStream, on the other hand, doesn't use any memory at all, it just writes directly to the file and lets the OS handle any buffering.

Regardless of which type of stream you use, one thing you could try is implement your own custom TStream class that has an internal fixed-size buffer and a pointer to your real destination TStream object. You can then pass an instance of your custom class to your algorithm. Have your class override the TStream::Write() method to copy the input data into its buffer until it fills up, then you can Write() the buffer to the destination TStream and clear the buffer. Your algorithm will never know the difference. Both TMemoryStream and TFileStream would benefit from the extra buffering - fewer larger writes means more efficient memory allocations and file I/O. For example:

type
  TMyBufferedStreamWriter = class(TStream)
  private
    fDest: TStream;
    fBuffer: array[0..4095] of Byte;
    fOffset: Cardinal;
  public
    constructor Create(ADest: TStream);
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    procedure FlushBuffer;
  end;

.

uses
  RTLConsts;

constructor TMyBufferedStreamWriter.Create(ADest: TStream);
begin
  fDest := ADest;
end;

function TMyBufferedStreamWriter.Read(var Buffer; Count: Longint): Longint;
begin
  Result := 0;
end;

function TMyBufferedStreamWriter.Write(const Buffer; Count: Longint): Longint;
var
  pBuffer: PByte;
  Num: Cardinal;
begin
  Result := 0;
  pBuffer := PByte(@Buffer);
  while Count > 0 do
  begin
    Num := Min(SizeOf(fBuffer) - fOffset, Cardinal(Count));
    if Num = 0 then FlushBuffer;
    Move(pBuffer^, fBuffer[fOffset], Num);
    Inc(fOffset, Num);
    Inc(pBuffer, Num);
    Dec(Count, Num);
    Inc(Result, Num);
  end;
end;

procedure TMyBufferedStreamWriter.FlushBuffer;
var
  Idx: Cardinal;
  Written: Longint;
begin
  if fOffset = 0 then Exit;
  Idx := 0;
  repeat
    Written := fDest.Write(fBuffer[Idx], fOffset - Idx);
    if Written < 1 then raise EWriteError.CreateRes(@SWriteError);
    Inc(Idx, Written);
  until Idx = fOffset;
  fOffset := 0;
end;

.

Writer := TMyBufferedStreamWriter.Create(RealStreamHere);
try
  ... write data to Writer normally as needed...
  Writer.FlushBuffer;
finally
  Writer.Free;
end;



回答3:


  1. Use a profiler to see where it is actually slow.
  2. If it is indeed because of multiple reallocation to increase the size of the stream, you can avoid that by setting the Size property to a big enough amount upfront.
  3. The only case where using a memory buffer could make an obvious difference is if you're using FileStreams, all other useable streams are already doing this for you.


来源:https://stackoverflow.com/questions/9930552/optimal-buffer-stream-write-process

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!