最佳缓冲流写入过程

时间:2012-03-29 17:41:07

标签: delphi delphi-7

我们拥有自己的数据流算法,包括一些元数据+记录+字段值。

目前我们使用TStream并写入以向流添加值。 现在我想知道这次使用某种技术是否可以更快地进行消耗操作。

修改:我们只是将数据追加到最后,而不是移动或寻找。

我想到的一些事情是:

  • 不使用Streams buf一些大内存分配缓冲区来复制数据,问题是如果我们超出缓冲区大小,那么我们必须重新定位到一些新的内存空间。
  • 使用预填充#0s的流到某个大小,然后开始添加值。理由是Tstream每次写一次都必须分配它自己的缓冲区(我不知道它是如何工作的,只是想知道......)

我们正在以#0#0#0#1的形式向TStream和二进制数据添加字符串。

然后通过TCP传输数据,因此不是写入文件。

那么最好的方法是什么?

3 个答案:

答案 0 :(得分:5)

首先,您假设TStream是瓶颈。您需要对代码进行概要分析,例如使用AQTime,以确定瓶颈的确切位置。不要做出假设。

其次,您实际使用的TStream是什么类型的? TMemoryStreamTFileStream?别的什么?不同的流类型处理内存不同。 TMemoryStream分配一个内存缓冲区,并在缓冲区填满时按预设的字节数增长。另一方面,TFileStream根本不使用任何内存,只是直接写入文件并让操作系统处理任何缓冲。

无论您使用哪种类型的流,您可以尝试的一件事是实现您自己的自定义TStream类,该类具有内部固定大小的缓冲区和指向您的真实目标TStream对象的指针。然后,您可以将自定义类的实例传递给算法。让您的类覆盖TStream::Write()方法将输入数据复制到其缓冲区中直到它填满,然后您可以Write()缓冲区到目标TStream并清除缓冲区。你的算法永远不会知道差异。 TMemoryStreamTFileStream都可以从额外的缓冲中受益 - 更少的大写意味着更高效的内存分配和文件I / O.例如:

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;

答案 1 :(得分:4)

  1. 使用分析器查看它实际上很慢的位置。
  2. 如果确实是因为多次重新分配以增加流的大小,则可以通过将Size属性预先设置为足够大的数量来避免这种情况。
  3. 使用内存缓冲区的唯一情况可能会产生明显的区别,如果你使用的是FileStreams,那么所有其他可用的流都已经为你做了这个。

答案 2 :(得分:2)

覆盖TMemoryStream并删除Size和Capacity的限制。并且不要调用TMemoryStream.Clear但是调用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;