一步步教你如何優(yōu)化Delphi字串查找

字號:

本人在編寫離線瀏覽器WebSeizer的過程中,用到大量字符串處理函數(shù),這是很耗CPU的一個處理過程,為了編寫出高效率的網(wǎng)頁解析引擎,對優(yōu)化代碼作了一番研究。
    1 、高精度的計時函數(shù)
    代碼優(yōu)化時需要用到精確的計時器。常用的有GetTickCount函數(shù),可以達到毫秒級的精度。但還是很不夠的,這時可以采用提高循環(huán)次數(shù)的辦法。另外,還有一個精度更高的定時——“高分辨率性能計數(shù)器”(high-resolution performance counter),它提供了兩個API函數(shù),取得計數(shù)器頻率的QueryPerformanceFrequency和取得計數(shù)器數(shù)值的QueryPerformanceCounter。實現(xiàn)原理是利用計算機中的8253,8254可編程時間間隔定時器芯片實現(xiàn)的。在計算機內(nèi)部有三個獨立的16位計數(shù)器。
    計數(shù)器可以以二進制或二—十進制(BDC)計數(shù)。計數(shù)器每秒產(chǎn)生1193180次脈沖,每次脈沖使計數(shù)器的數(shù)字減一,產(chǎn)生頻率是可變的,用QueryPerformanceFrequency可以得到,一般情況下都是1193180。QueryPerformance Counter可以得到當前的計數(shù)器值。所以只要你的計算機
    夠快,理論上精度可以達到1/1193180秒。
    2 、代碼優(yōu)化實例
    下面以一個自定義的字符串函數(shù)的為例,說明代碼優(yōu)化過程。
    Delphi提供的字符串函數(shù)里有一個Pos函數(shù),它的定義是:
    function Pos(Substr: string; S: string): Integer;
    它的作用是在字符串S中查找字符串Substr,返回值是Substr在S中第一次出現(xiàn)的位置,如果沒有找到,返回值為0。
    在本人編寫WebSeizer軟件(天空軟件站有下載)過程中,Pos已經(jīng)不能滿足要求。一方面:在處理網(wǎng)頁中的字符串時,要求對大小寫不敏感,即< h t m l > 和代表的含義完全一樣。另一方面:我們還要求有一個函數(shù),返回值是Substr在S中最后一次出現(xiàn)的位置,而不是第一次出現(xiàn)的位置。下面是這個函數(shù)的未經(jīng)優(yōu)化的代碼。
    function RightPos(const Substr,S: string): Integer;
    var
    iPos: Integer;
    TmpStr:string;
    begin
    TmpStr:=s;
    iPos := Pos(Substr,TmpStr); Result:=0;
    //查找Substr第一次出現(xiàn)位置
    while iPos<>0 do
    begin
    Delete(TmpStr,1,iPos+length(Substr)-1);
    //刪除已經(jīng)查找過的字符
    Result:=Result+iPos;
    iPos := Pos(Substr,TmpStr); //查找Substr出現(xiàn)位置
    if iPos=0 then break;
    Result:=Result+length(Substr)-1;
    end;
    end;
    這個函數(shù)里,用到了Delete函數(shù),我們再來看一看System.pas文件里Delete函數(shù)的實現(xiàn)過程,請看下面代碼:
    procedure _LStrDelete{ var s : AnsiString; index, count : Integer };
    asm
    { EAX Pointer to s }
    { EDX index }
    { ECX count }
    PUSH EBX
    PUSH ESI
    PUSH EDI
    MOV EBX,EAX
    MOV ESI,EDX
    MOV EDI,ECX
    CALL UniqueString
    MOV EDX,[EBX]
    TEST EDX,EDX { source already empty:
    nothing to do }
    JE @@exit
    MOV ECX,[EDX-skew].StrRec.length
    { make index 0-based, if not in [0 .. Length(s)-1]
    do nothing }
    DEC ESI
    JL @@exit
    CMP ESI,ECX
    JGE @@exit
    { limit count to [0 .. Length(s) - index] }
    TEST EDI,EDI
    JLE @@exit
    SUB ECX,ESI { ECX = Length(s) - index
    }
    CMP EDI,ECX
    JLE @@1
    MOV EDI,ECX
    @@1:
    { move length - index - count characters from
    s+index+count to s+index }
    SUB ECX,EDI { ECX = Length(s) - index
    - count }
    ADD EDX,ESI { EDX = s+index }
    LEA EAX,[EDX+EDI] { EAX = s+index+count }
    CALL Move
    { set length(s) to length(s) - count }
    MOV EDX,[EBX]
    MOV EAX,EBX
    MOV EDX,[EDX-skew].StrRec.length
    SUB EDX,EDI
    CALL _LStrSetLength
    @@exit:
    POP EDI
    POP ESI
    POP EBX
    end;
    Delete 函數(shù)中,有這兩句:CALL Move和CALL_LstrSetLength。其中Move函數(shù)是將一個內(nèi)存塊拷貝到另一個地址,LstrSetLength函數(shù)將改變字符串的長度,其中也有對內(nèi)存進行分配的代碼。這些對內(nèi)存進行操作的函數(shù)都是極其消耗CPU運行時間的,所以Delete函數(shù)也是一個極其消耗CPU運行時間的函數(shù)。為了盡量避免使用這些函數(shù),我對自定義函數(shù)RightPos進行了改寫。