对TMemoryStream的一些改进

怎么又是关于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存放经常要进行查找的数据。。。。等等,看到自己开发的软件在速度上有了质的飞跃,实在是很有成就感啊。