怎麼又是關於Stream的,呵呵,應該說只是最近比較關心程序的效率問題,而我對Stream其實並沒有什麼特別的研究,只是自己發現了一些新的用法,希望能對大家有用而已。
事情的起因還是那個破爛電子相冊軟件,今天又發現了一個可改進之處,有一段程序我原來是這麼寫的:
procedure CreateFile(const AFileName:String;const AStream:TMemoryStream);
var
FileStream:TMemoryStream;
begin
ShowProgressForm(nil);
FileStream:=TMemoryStream.Create();
try
FileStream.LoadFromFile(AFileName);
FileStream.Position:=FileStream.Size;
AStream.Position:=0;
FileStream.CopyFrom(AStream,AStream.Size);
FileStream.SaveToFile(AFileName);
finally
FileStream.Free;
end;
end;
為了完成將一個TMemoryStream追加到一個文件中的任務,我使用了另一個TMemoryStream,讓它先打開文件,然後使用CopyFrom()函數,從原始Stream中加入數據,最後再保存到文件中。
其中最糟糕的就是CopyFrom()函數,它會開辟一塊新的內存,先調用ReadBuffer()函數,從源Stream中取得數據,再調用自身的WriteBuffer()函數,寫到自身的Buffer中,最後再釋放這塊臨時內存,這些過程可以看這段代碼:
function TStream.CopyFrom(Source: TStream; Count: Int64): Int64;
const
MaxBufSize = $F000;
var
BufSize, N: Integer;
Buffer: PChar;
begin
if Count = 0 then
begin
Source.Position := 0;
Count := Source.Size;
end;
Result := Count;
if Count > MaxBufSize then BufSize := MaxBufSize else BufSize := Count;
GetMem(Buffer, BufSize);
try
while Count <> 0 do
begin
if Count > BufSize then N := BufSize else N := Count;
Source.ReadBuffer(Buffer^, N);
WriteBuffer(Buffer^, N);
Dec(Count, N);
end;
finally
FreeMem(Buffer, BufSize);
end;
end;
而且,不知道為何,Delphi自己提供的Move()函數在內存拷貝時顯得特別的慢。最後導致的結果就是,我在將30MB左右的數據寫入文件時,會花半分鐘的時間。
知道了問題所在,那麼要加速這個過程就很簡單了,首先當然要避免內存拷貝,所以我決心去掉那個累贅的FileStream,讓原始Stream自己將內存數據寫入到文件,那樣不是就可以了嗎?
但是無論是TMemoryStream,還是TFileStream,都只提供將數據完全寫入一個文件的功能,而我需要的則是追加功能,呵呵,這個簡單,自己打開文件,然後WriteFile()就可以了,所以最終的解決方法就是:
從TMemoryStream繼承出一個新類,暫且叫做TMemoryStreamEx,加入一個新的方法,叫做:AppendToFile(),可以將內存數據完全追加到已存在的文件內,函數內容如下:
procedure TMemoryStreamEx.AppendToFile(const AFileName:String);
var
FileHandle:LongWord;
CurPos:LongWord;
BytesWritten:LongWord;
begin
FileHandle:=CreateFile(PChar(AFileName),GENERIC_WRITE,0,nil,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
if FileHandle=INVALID_HANDLE_VALUE then begin
raise MemoryStreamExException.Create('Error when create file');
end;
try
CurPos:=SetFilePointer(FileHandle,0,nil,FILE_END);
LockFile(FileHandle,CurPos,0,Size,0);
try
BytesWritten:=0;
if not WriteFile(FileHandle,Memory^,Size,BytesWritten,nil) then begin
raise MemoryStreamExException.Create('Error when write file');
end;
if (Size<>BytesWritten) then begin
raise MemoryStreamExException.Create('Wrong written size');
end;
finally
UnlockFile(FileHandle,CurPos,0,Size,0);
end;
finally
CloseHandle(FileHandle);
end;
end;
好了,替換掉原來的那段程序,新的程序變為:
procedure TExeExporter.CreateExecutableFile(const AFileName:String;const AStream:TMemoryStreamEx);
begin
AStream.AppendToFile(AFileName);
end;
就那麼簡單,速度也縮短到僅僅2-3秒了。
最近單位做的一系列軟件也在進行提速優化,使用了好多方法,自己管理內存(減少malloc的調用次數),使用HashTable存放經常要進行查找的數據。。。。等等,看到自己開發的軟件在速度上有了質的飛躍,實在是很有成就感啊。