事情的起因還是那個破爛電子相冊軟件,今天又發(fā)現(xiàn)了一個可改進之處,有一段程序我原來是這么寫的: 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追加到一個文件中的任務(wù),我使用了另一個TMemoryStream,讓它先打開文件,然后使用CopyFrom()函數(shù),從原始Stream中加入數(shù)據(jù),最后再保存到文件中。 其中最糟糕的就是CopyFrom()函數(shù),它會開辟一塊新的內(nèi)存,先調(diào)用ReadBuffer()函數(shù),從源Stream中取得數(shù)據(jù),再調(diào)用自身的WriteBuffer()函數(shù),寫到自身的Buffer中,最后再釋放這塊臨時內(nèi)存,這些過程可以看這段代碼: 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()函數(shù)在內(nèi)存拷貝時顯得特別的慢。最后導(dǎo)致的結(jié)果就是,我在將30MB左右的數(shù)據(jù)寫入文件時,會花半分鐘的時間。
知道了問題所在,那么要加速這個過程就很簡單了,首先當(dāng)然要避免內(nèi)存拷貝,所以我決心去掉那個累贅的FileStream,讓原始Stream自己將內(nèi)存數(shù)據(jù)寫入到文件,那樣不是就可以了嗎? 但是無論是TMemoryStream,還是TFileStream,都只提供將數(shù)據(jù)完全寫入一個文件的功能,而我需要的則是追加功能,呵呵,這個簡單,自己打開文件,然后WriteFile()就可以了,所以最終的解決方法就是: 從TMemoryStream繼承出一個新類,暫且叫做TMemoryStreamEx,加入一個新的方法,叫做:AppendToFile(),可以將內(nèi)存數(shù)據(jù)完全追加到已存在的文件內(nèi),函數(shù)內(nèi)容如下: 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;
好了,替換掉原來的那段程序,新的程序變?yōu)椋?BR>procedure TExeExporter.CreateExecutableFile(const AFileName:String;const AStream:TMemoryStreamEx); begin AStream.AppendToFile(AFileName); end; 就那么簡單,速度也縮短到僅僅2-3秒了。