zoukankan      html  css  js  c++  java
  • Tlist删除技巧

    二、    从TList开始分析……

    为了写一个更好的性能ISAPI Filter,我需要更快速地从TList中删除部分连续的Item。比如这样的一段代码:

    var p : pChar = 'abcdefgh';
    procedure TestDelFromTList;
    var t1 : TList;
        i  : integer;
        maxI : integer;
    begin
      t1 := tlist.create;
      t1.count := 100000;
      for i:=0 to t1.count-1 do t1[i] := p;
      maxI := t1.count-10-1; //最后一个结点
      for i:= maxI downto 10 do t1.delete(i);
      //正向删除
      //for i:=10 to t2.count-10-1 do t2.delete(10);
    end;

    这段代码是初始化一个100000个结点的List,然后删除其中的第10个到倒数第10个。这段代码是逆向的,这样的删除速度比较快。如果换成正向删除(已经注释掉),则速度就慢得非常多了。

    这样的删除是正常的算法。用测效率的程序测试:逆向删除算法的耗时是21.66个毫秒,则正向删除的耗时却能达到58099.02个毫秒。速度慢了2680倍!!!

    但这样就很快了么?不是!我认为就算是逆向删除的速度也并不是快的。

    分析TList这个类的源码,我们可以看到,它是这样写的(我加入了注释):

    procedure TList.Delete(Index: Integer);
    var
      Temp: Pointer;
    begin
    if (Index < 0) or (Index >= FCount) then //判定Index值是否超界
    Error(@SListIndexError, Index);
      Temp := Items[Index];                        //取待删除结点
      Dec(FCount);                                   //Count减一
    if Index < FCount then                       //将待删除结点后的Buffer提前
    System.Move(FList^[Index + 1], FList^[Index],(FCount - Index) * 
    SizeOf(Pointer)); 
    if Temp <> nil then                           //发通告
    Notify(Temp, lnDeleted); 
    end;

    由于在TList类是将全部的结点指针存放在FList这个动态数组的指针中,所以只需要将Index+1之后的内存块向前移4个字节,即SizeOf(Pointer),即可实现Index结点的删除。

    但是,如果使用这样来删除成批连续的(N个)结点,则要实现N次system.move()操作,操作的内存块的大小决定了system.move()操作的耗时,而Index值越小的的结点在FList中越靠前,则system.move()要操作的内存块也就越大。这就是我认为上述成批删除效率不高的原因,也是正向删除比逆向删除的耗时慢了慢了2680倍的原因。

    对于成批删除,理想的算法是从index+len结点开始位置,向前移动count-index-len个结点,这样,就能够一次完成全部的结点移动,实现删除操作。这个思路非常好,至少我认为是这样。为此,我实现了下面的代码:

    procedure CutList(aList:TList; left,len:integer);
    begin
      with aList do begin
    System.Move(List^[left+len], List^[left], (Count-left-len) * 
    SizeOf(Pointer));
        count := count-len;
      end;
    end;

    这段代码的功能是在TList.List这个Buffer中,将删除后的剩余结点直接移动到Left这个位置上,从而完成全部的移动操作。

    然后,我们再设count := count-len;来使用个数减少,从而完成了成批量的删除。

    好的,如果一切正常,算法的速度将大幅度提升!OHHH,美妙的想法!

    但是,真的是这样么?我再用效率测试程序来测试了一轮,结果是这样的:

    1.    测试数据为10万个结点,则逆向删除算法耗时为20.56毫秒,CutList()函数耗时9.69毫秒;

    2.    测试数据为100万个结点,则逆向删除算法耗时为209.13毫秒,CutList()函数耗时98.01毫秒。

    速度比逆向算法提高了一倍,而且应该注意到,CutList()的耗时仍然随数据量的增大而等比例的增大!!!而从CutList()函数的实现来看,数据量增大,算法耗时应该只增加极少才对。

    要知道,只加快一倍速度的CutList(),并不是我所想要的!!!但为什么CutList()函数得不到更高的性能呢???

    用加法的方式去爱人, 用减法的方式去怨恨, 用乘法的方式去感恩。 人生,最重要的不是得失, 而是拥有一颗善待自己的平常心!
  • 相关阅读:
    UI Automation
    TextBlock can't be find in DataTemplate when use Coded UI Test
    windows Phone本地化
    需要反射时使用dynamic
    npm发包流程
    mockjs介绍
    oracle job不自动执行,手动可以执行
    第一章 线性结构顺序存储结构
    Java连接MySQL数据库
    阿里云服务器部署Java开发环境JDK和mysql
  • 原文地址:https://www.cnblogs.com/sunday-night/p/6169325.html
Copyright © 2011-2022 走看看