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

对tmemorystream的一些改进_delphi教程的更多相关文章

  1. (转载)c++builder/delphi中透明panel及透明窗口的实现方法_delphi教程

    c++builder/delphi中透明panel及透明窗口的实现方法_delphi教程 可能大多数程序员会问:透明窗口,特别是透明Panel有什么应用价值呢?可别小看它们哦,下面我就来讲讲他们的巨大 ...

  2. 对TMemoryStream的一些改进(用到了LockFile)

    对TMemoryStream的一些改进 怎么又是关于Stream的,呵呵,应该说只是最近比较关心程序的效率问题,而我对Stream其实并没有什么特别的研究,只是自己发现了一些新的用法,希望能对大家有用 ...

  3. 将多个jpg文件以追加形式合并成一个文件_delphi教程 bmp 合并 http://www.west.cn/www/info/58058-1.htm

    将多个jpg文件以追加形式合并成一个文件_delphi教程 作者:网友供稿 点击:0 西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!云服务器 ...

  4. 用数据表创建树_delphi教程

    数据库结构:字段 类型ID 整型 索引(无重复)name 文本father 整型 //tree初始化procedure TForm1.FormActivate(Sender: TObject);var ...

  5. 中文转码器的工作原理_delphi教程

    最近在做Delphi下的简体与繁体转换, 发现Windows2000自带的工具"中文转码器"很好用, 不仅可以转内码(BIG5-->GBK), 还可以将繁体字转为简体字(如: ...

  6. 我的dbtreeview–treeview直接连接数据表_delphi教程

    unit Unit1; interface uses  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs ...

  7. 在Swift中应用Grand Central Dispatch(上)转载自的goldenfiredo001的博客

    尽管Grand Central Dispatch(GCD)已经存在一段时间了,但并非每个人都知道怎么使用它.这是情有可原的,因为并发很棘手,而且GCD本身基于C的API在 Swift世界中很刺眼. 在 ...

  8. fedora20安装spin以及用户界面ispin

    (博客园-番茄酱原创) (最近感觉用make会出现库错误,所以改进了教程,把之前的make步骤省掉了,直接下载可执行文件进行配置最简单啦...) 1.首先,下载对应版本的spin,我64位的fedor ...

  9. 【iOS】Swift GCD-上

    尽管Grand Central Dispatch(GCD)已经存在一段时间了,但并非每个人都知道怎么使用它.这是情有可原的,因为并发很棘手,而且GCD本身基于C的API在Swift世界中很刺眼. 在这 ...

随机推荐

  1. iOS-SDWebImage的原理以及使用流程

    SDWebImage 支持异步的图片下载+缓存,提供了 UIImageView+WebCacha 的 category,方便使用.SDWebImage加载图片的流程: 1. 入口 setImageWi ...

  2. hdu 1575 Tr A (二分矩阵)

    Tr A Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  3. [bzoj] 2694 Lcm || 莫比乌斯反演

    原题 定义整数a,b,求所有满足条件的lcm(a,b)的和: 1<=a<=A 1<=b<=B ∀n>1,n2†gcd(a,b)(即任意n>1,\(n^2\)不是gc ...

  4. Failed to resolve:com.android.support:appcompat-v7

    http://blog.csdn.net/mhl18820672087/article/details/78385361

  5. CMU Bomblab 答案

    室友拉我做的... http://csapp.cs.cmu.edu/3e/labs.html Border relations with Canada have never been better. ...

  6. PHP 抽象类,接口,抽象方法,静态方法

    1.Abstract class(抽象类) 抽象类是指在 class 前加了 abstract 关键字且存在抽象方法(在类方法 function 关键字前加了 abstract 关键字)的类. 抽象类 ...

  7. xcode 10 新特性

    这里主要介绍一下Xcode10 版本主要更新的内容.随着iOS12的发布,Xcode10已经可以从Mac App Store下载.Xcode10包含了iOS12.watchOS 5.macOS10.1 ...

  8. SQL优化之一

    使用union代替or,可以提升查询效率. 使用or时,会自动放弃已有的索引

  9. WebComponents001

    Sample1: ShadowDom 隔离style,替换显示内容 <button>Hello, world!</button> <script> var host ...

  10. Codeforces Round #475 Div. 2 A B C D

    A - Splits 题意 将一个正整数拆分成若干个正整数的和,从大到小排下来,与第一个数字相同的数字的个数为这个拆分的权重. 问\(n\)的所有拆分的不同权重可能个数. 思路 全拆成1,然后每次将2 ...