起步软件技术论坛-X3

 找回密码
 立即注册
搜索
查看: 716|回复: 8

[分享]速度专题1-单个功能的速度优化

[复制链接]
发表于 2008-2-27 08:52:49 | 显示全部楼层 |阅读模式
当某个功能速度慢时候,首先要定位速度慢的点,比如是进入功能慢,做某项操作慢,还是流转慢,还是关闭功能慢等。
然后,详细分析瓶颈点,找到慢的原因,针对性的解决。

一般解决速度慢的问题,掌握以下原则:
  • 尽量减少跟服务器的交互次数
  • 尽量减少一次交互的数据量
  • 将集中的长时间交互变成多次短时间的交互
  • 大量数据操作时候屏蔽界面的交互
  • 长时间的操作给出界面的变化,表示正在进行长时间的操作
    [/list=1]
回复

使用道具 举报

 楼主| 发表于 2008-2-27 09:00:11 | 显示全部楼层

尽量减少跟服务器的交互次数

  • 使用数据表缓存
      对于使用频繁但是不经常修改的数据,可以设置数据表的使用缓存属性,把数据缓存到本地,这样每次数据请求都不会出现跟服务器的交互
  • 在循环外提交数据
      当循环往某一个数据集中增加数据这类的代码,有人写成
      DataSet1.First;
      while not DataSet1.Eof do
      begin
        DataSet2.Append;
        DataSet2.Fields[0].AssignValue(DataSet1.Fields[0]);
        ...
        DataSet2.Post;
        DataSet.ApplyUpdates;  //每一次增加数据都会提交给服务端
        DataSet1.Next;
      end;
    应该改成
      DataSet1.DisableControls;
      DataSet2.DisableControls;
      try
        DataSet1.First;
        while not DataSet1.Eof do
        begin
          DataSet2.Append;
          DataSet2.Fields[0].AssignValue(DataSet1.Fields[0]);
          ...
          DataSet2.Post;
          DataSet1.Next;
        end;
        DataSet.ApplyUpdates;  //增加了所有数据以后,一次提交
      finally
        DataSet1.EnableControls;
        DataSet2.EnableControls;
      end;
    (注意:如果数据量太大,一次提交会造成数据包太大,如果网络条件不好可能会因为丢包而保存不成功,可以用循环计数,没n条数据提交一次)
  • 错误的代码:
      DataSet.SQLFiltered := False;   //执行一次取数据操作,而且是没有SQLFilter条件限制的数据
      DataSet.SQLFilter := ...;
      DataSet.SQLFiltered := True;  //又执行一次取数据操作
    应该是
      DataSet.SQLFilter := ...;
      DataSet.SQLFiltered := True;  //只执行一次取数据操作
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-27 09:05:01 | 显示全部楼层

尽量减少一次交互的数据量

  • 按照业务过滤数据或者设置数据集的分组取数据属性
    在做系统设计时候,应该对每一个表的大致数据量有一个预估,对于数据量很大的表,一定要从业务上对数据做过滤,而不是不加任何条件的选择数据。如果数据量很难估计,或者业务上对数据的过滤不能保证数据量减少,可以在数据集上定义分页取数据。
  • 用索引数据集和详细数据集来减少一次的数据量
    如果数据集中数据字段非常多,可以用一个包含少量字段索引数据集,用Grid表现,一个包含所有需要字段的详细数据集,定义这两个数据集是主从关系,这样详细数据集一次只会取一条数据。
    尤其是包含二进制字段的时候,一个字段的数据量都会很大,更要这样,只在详细数据集中显示二进制字段。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-27 09:07:53 | 显示全部楼层

将集中的长时间交互变成多次短时间的交互

通常进入功能慢在于打开功能时候,同时打开了很多的数据集,这时候可以定义DocView的OptimizeLoadDoc属性,如果多页显示的时候,只有第一页文档被加载进来,也只有第一页文档中用到的数据集被打开了,其他页的数据集都还是处于关闭状态。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-27 09:10:40 | 显示全部楼层

大量数据操作时候屏蔽界面的交互

  • 当有大量的数据操作时候,用DisableControls屏蔽数据控件,可以大大的提交效率。
    DataSet.DisableControls;
    try
      DataSet.First;
      while not DataSet.Eof do
      begin
        ...
        DataSet.Next;
      end;
    finally
      DataSet.EnableControls;
    end;
  • 复杂控件一般有BeginUpdate和EndUpdate方法,在大量修改控件内容之前和之后调用,避免在修改中控件重画
  • 设置字段的ReadOnly等属性时候,会引发字段对应控件的相应变化,如果有多个字段同时设置这类的属性,也可以先DisableControls。
    DataSet.DisableControls;
    try
      DataSet.Field[0].ReadOnly := True;
      DataSet.Field[1].ReadOnly := False;
      DataSet.Field[2].ReadOnly := True;
      DataSet.Field[3].ReadOnly := False;
      DataSet.Field[4].ReadOnly := False;
    finally
      DataSet.EnableControls;
    end;
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-27 09:12:18 | 显示全部楼层

长时间的操作给出界面的变化,表示正在进行长时间的操作

当无法避免的长时间计算时候,可以把鼠标变成漏斗,显示一个进度条等,让用户看到正在进行的操作,可以增加用户等待的耐心。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-27 09:42:07 | 显示全部楼层

定位瓶颈点

用SystemMonitor可以分析客户端与服务端的每一次交互做了什么,传输的大小等,可以分析瓶颈点是否在网络或者服务器上。
如果瓶颈点在客户端,比如有一系列长时间的运算,那么定位速度瓶颈就比较困难了,因为单步执行无法只能定位错误,不能分析速度。可以在几个关键点上记录下来发生的时间,然后分析这些时间差来找速度慢的点。
例如:定义一个记录日志的函数
procedure AddLog(msg: String);
var
  fs: TStream;
  wr: TWriter;
begin
  fs := nil;
  wr := nil;
  try
    try
      fs := TFileStream.Create('c:\debug.txt', SysUtils.fmOpenWrite);
    except
      fs := TFileStream.Create('c:\debug.txt', Classes.fmCreate);
    end;
    fs.Seek(0, TSeekOrigin.soEnd);
    wr := TWriter.Create(fs, 1024);
    wr.WriteStr( SysUtils.Format('happend:%d  %s'#13#10, [Borland.Delphi.Windows.GetTickCount, msg]));
  finally
    wr.Free;
    fs.Free;
  end;
end;

procedure AddLog1(msg: String);
var
  fs: TStrings;
begin
  fs := TStringList.Create;
  try
    try
      fs.LoadFromFile('c:\debug.txt');
    except
    end;
    if (fs.Count>0) and (Length(fs[fs.Count-1])=0) then
      fs.Delete(fs.Count-1);
    fs.Add(SysUtils.Format('happend:%d  %s'#13#10, [Borland.Delphi.Windows.GetTickCount, msg]));
    fs.SaveToFile('c:\debug.txt');
  finally
    fs.Free;
  end;
end;

在需要分析的地方调用这个函数来记录
  AddLog('start');
  proc1();
  AddLog('after proc1');
  proc2();
  AddLog('after proc2');
  DataSet.First;
  while not DataSet.Eof do
  begin
    ...
    DataSet.Next;
  end;
  AddLog('after DataSet loop);

  AddLog('end');

执行了这段代码以后,只要拿出c:\debug.txt来分析一下就知道速度瓶颈点了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-27 10:03:44 | 显示全部楼层

“进入功能慢”的常见解决方法

  • 如果是多页文档,比如用PageControl,定义每一个文档的OptimizeLoadDoc属性,减少交互次数
  • 定义数据集的分页取数据属性,减少数据量
  • 在功能上定义信息策略,定义数据集的默认过滤,减少数据量
  • 检查功能主窗体的OnCreate、OnShow事件,是否有耗时的动作
  • 检查在功能进入时,是否触发了自己未知的事件,比如:TGraphicPageControl在创建时候会触发每一个TabSheet的OnShow事件
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-2-27 10:11:32 | 显示全部楼层

“流转慢”的常见解决方法

  • 参考 http://bbs.justep.com/forum.php?mod=viewthread&tid=19618 看看流转过程中的每一个事件是否有耗时的动作
  • 组织机构表达式最终是通过执行代码生成的SQL语句执行的,太复杂的组织机构表达式会生成超级复杂的SQL语句,造成SQL执行慢,可以用监控得到组织机构对应的SQL语句,如果这个语句执行时间太长,就要考虑换一个组织机构表达式来实现了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Justep Inc.

GMT+8, 2025-7-7 10:58 , Processed in 0.037445 second(s), 15 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表