2012-03-29 56 views
3

我们有自己的数据流算法,其中包含一些元数据+记录+字段值。最佳缓冲流写入过程

当前我们使用TStream并写入以向流中添加值。 现在我想知道是否可以通过使用一些技术来加快这个时间的cosuming操作。

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

有些事情,我在想的是:

  • 不使用流BUF一些大的内存分配的缓冲区将数据复制到的问题是,如果我们超越了缓冲区的大小,那么我们就必须搬迁到一些新的内存空间。
  • 使用预先填充#0s的流来达到某个尺寸,然后开始添加值。理由是Tstream在每次写入时都必须分配它自己的缓冲区(我不知道它是如何工作的,只是想知道...)

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

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

那么最好的方法是什么?

+0

你说的是内存流吗? – 2012-03-29 17:56:55

+0

如果数据量很大(比如超过200MB),那么我推荐一个TFileStream,否则TMemoryStream会是最快最容易的方式。如果您选择使用TFile | MemoryStream并且您需要保留信息(记录计数等),请在Stream的开头添加一个标题,并在每次更新后将光标位置重置为开头,读取并覆盖Stream的标题。位置:= Stream.Size; < - bam你在流的末尾,继续追加数据。如果有任何删除记录的机会,那么每条记录都需要一个包含标志的标题(Deleted:Boolean) – ComputerSaysNo 2012-03-29 18:23:39

+0

@Dorin:我们使用套接字发送,所以TFileStream似乎不是一个选项。问题不在于如何使用stream + write来做到这一点,我们已经在使用它,问题是如果有一些方法可以更快地做到这一点。 – 2012-03-29 19:09:52

回答

2

覆盖TMemoryStream并删除大小和容量的限制。并且不要调用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; 
4
  1. 使用Profiler查看它实际上很慢的位置。
  2. 如果确实是因为多次重新分配以增加流的大小,您可以通过将Size属性设置为足够大的数量来避免这种情况。
  3. 使用内存缓冲区的唯一情况是显而易见的区别是,如果您使用的是FileStreams,则所有其他可用的流已经为您执行此操作。
+0

只读TStringStream的文档(Delphi 7)可以设置Size属性,我们可以使用它。我们还应该探索Profiler路由(从未使用过) – 2012-03-29 19:18:25

+0

@DanielLuyo,我只是看了D7的源代码,TStream有一个公共的Size属性,任何后代都可以使用(读或写)。 – 2012-03-29 20:35:15

4

首先,假设TStream是瓶颈。您需要分析您的代码,例如AQTime,以确定瓶颈的真正位置。不要做出假设。

其次,你实际使用什么类型的TStreamTMemoryStreamTFileStream?还有别的吗?不同的流类型处理内存的方式不同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;