2012-03-29 65 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;