zoukankan      html  css  js  c++  java
  • 对tmemorystream的一些改进_delphi教程

    http://www.cnblogs.com/linyawen/archive/2010/12/11/1903072.html

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

  • 相关阅读:
    多选菜单shell脚本
    获取nginx日志状态码百分比脚本
    Linux内核参数优化
    deploy.sh
    Docker常用命令速查
    Docker架构
    修改Docker默认存储路径
    redis安装与基本使用
    js实现汉字转拼音
    android:Android 6.0+ 权限控制代码封装
  • 原文地址:https://www.cnblogs.com/tc310/p/5266053.html
Copyright © 2011-2022 走看看