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. Python创建目录文件夹

    Python对文件的操作还算是方便的,只需要包含os模块进来,使用相关函数即可实现目录的创建. 主要涉及到三个函数 1.os.path.exists(path) 判断一个目录是否存在 2.os.mak ...

  2. exit和die的区别

    网上搜索die与exit两个函数的区别,大部分的"标准答案"都是说die是退出并释放内存,exit是退出但不释放内存. 这个解释显然是错的,PHP手册中已经说过"die ...

  3. [剑指Offer] 8.跳台阶

     题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法. [思路]与斐波那契数列类似 class Solution { public: int jumpF ...

  4. HTML5<canvas>标签:使用canvas元素在网页上绘制四分之一圆(3)

    前几天自己做了个四分之一的圆,放到手机里面测试.效果不是很好.于是今天通过查资料,找到了canvas.自己研究了一天,发现可以使用canvas画圆.代码如下: <!doctype html> ...

  5. C#判断字符串是否为数字字符串

    在进行C#编程时候,有的时候我们需要判断一个字符串是否是数字字符串,我们可以通过以下两种方法来实现.[方法一]:使用 try{} catch{} 语句.      我们可以在try语句块中试图将str ...

  6. Luogu3952 NOIP2017时间复杂度

    搞一个栈模拟即可.对比一下和一年前考场上的代码233 //2018.11.8 #include<iostream> #include<cstdio> #include<c ...

  7. 【题解】ZJOI2008骑士

    树型打牌:洛谷P2607 这道题目一开始没有想到解法,只是想到没有上司的舞会,觉得十分的类似呀. 之后发现:n个点,n条边,只要删去一条边,就变成了和上题一模一样的做法. 那么考虑删去的这条边,实际上 ...

  8. [洛谷P1951]收费站_NOI导刊2009提高(2)

    题目大意:有一张$n$个点$m$条边的图,每个点有一个权值$w_i$,有边权,询问从$S$到$T$的路径中,边权和小于$s$,且$\max\limits_{路径经过k}\{w_i\}$最小,输出这个最 ...

  9. 【BZOJ 1647】[Usaco2007 Open]Fliptile 翻格子游戏 模拟、搜索

    第一步我们发现对于每一个格子,我们只有翻和不翻两种状态,我们发现一旦确定了第一行操作,那么第二行的操作也就随之确定了,因为第一行操作之后我们要想得到答案就得把第一行全部为0,那么第二行的每一个格子的操 ...

  10. ubuntu使用su切换root用户提示“认证失败”

    在虚拟机上安装了ubuntu,安装时提示设置密码,也设置了,但是在终端操作时,遇到权限不够的问题,于是就想到就是要切换root用户,获取最高权限. 当我使用 su 切换到root用户时,提示我输入密码 ...