1 、高精度的計時函數
計數器可以以二進制或二—十進制(BDC)計數。計數器每秒産生1193180次脈沖,每次脈沖使計數器的數字減一,産生頻率是可變的,用QueryPerformanceFrequency可以得到,一般情況下都是1193180。QueryPerformance Counter可以得到目前的計數器值。是以隻要你的計算機
夠快,理論上精度可以達到1/1193180秒。
2 、代碼優化執行個體
下面以一個自定義的字元串函數的為例,說明代碼優化過程。
Delphi提供的字元串函數裡有一個Pos函數,它的定義是:
它的作用是在字元串S中查找字元串Substr,傳回值是Substr在S中第一次出現的位置,如果沒有找到,傳回值為0。
<a href="http://www.yesky.com/key/303/100303.html">var</a>
iPos: Integer;
TmpStr:string;
begin
TmpStr:=s;
iPos := Pos(Substr,TmpStr); Result:=0;
//查找Substr第一次出現位置
while iPos<>0 do
begin
Delete(TmpStr,1,iPos+length(Substr)-1);
//删除已經查找過的字元
Result:=Result+iPos;
iPos := Pos(Substr,TmpStr); //查找Substr出現位置
if iPos=0 then break;
Result:=Result+length(Substr)-1;
end;
end;
這個函數裡,用到了Delete函數,我們再來看一看System.pas檔案裡Delete函數的實作過程,請看下面代碼:
procedure _LStrDelete{ var s : AnsiString; index, count : Integer };
asm
{ EDX index }
{ ECX count }
PUSH EBX
PUSH ESI
PUSH EDI
MOV ESI,EDX
MOV EDI,ECX
CALL UniqueString
MOV EDX,[EBX]
nothing to do }
JE @@exit
MOV ECX,[EDX-skew].StrRec.length
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
@@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 }
CALL Move
{ set length(s) to length(s) - count }
MOV EAX,EBX
MOV EDX,[EDX-skew].StrRec.length
SUB EDX,EDI
CALL _LStrSetLength
@@exit:
POP EDI
POP ESI
POP EBX
Delete 函數中,有這兩句:CALL Move和CALL_LstrSetLength。其中Move函數是将一個記憶體塊拷貝到另一個位址,LstrSetLength函數将改變字元串的長度,其中也有對記憶體進行配置設定的代碼。這些對記憶體進行操作的函數都是極其消耗CPU運作時間的,是以Delete函數也是一個極其消耗CPU運作時間的函數。為了盡量避免使用這些函數,我對自定義函數RightPos進行了改寫。
修改後不再使用Delete及Pos函數,直接通過指針對記憶體操作,提高了效率。
function RightPosEx(const Substr,S: string): Integer;
var
iPos: Integer;
TmpStr: string;
i, j, lvlen: Integer;
PCharS, PCharSub: PChar;
PCharS := PChar(s); //将字元串轉化為PChar格式
PCharSub := PChar(Substr);
Result := 0;
lvlen := length(Substr);
for i := 0 to length(S) - 1 do
begin
for j := 0 to lvlen - 1 do
begin
if PCharS[i + j] <> PCharSub[j] then break;
end;
if j = lvlen then Result := i + 1;
end;
請看第一句PCharS:=PChar(s),它的作用是将Delphi字元串強制轉化為PChar 格式(PChar 是Windows中使用的标準字元串,不包含長度資訊,使用0為結束标志),并得到指向PChar字元串的指針PcharS。
下面就要對自定義函數的運作時間進行測量,為了提高測量的精度,減小随機性,我們計算重複10000次所需的時間。代碼如下:
i,len,iPos: Integer;
PerformanceCount1,PerformanceCount2,Count:int64;
len:=10000; //重複次數
QueryPerformanceCounter(PerformanceCount1);//開始計數
for i:=0 to len-1 do
iPos:=RightPos(’12’,Edit1.Text); //被測試的函數
QueryPerformanceCounter(PerformanceCount2); //結束計數
Count:=(PerformanceCount2-PerformanceCount1);
Label1.Caption:=inttostr(iPos)+’ time=’+inttostr(Count);
End;
我的配置是Duron700,256M記憶體,測試中RightPos函數重複了10000遍,RightPos使用的參數為:Substr=12,S=Edit12ewew12tet。得到的測量結果是Count=217000,又對其他幾個函數作了對比,結果如下:
函數名
重複次數
QueryPerformanceCounter 計數值
Pos
10000
150000
RightPos
217000
RightPosEx
160000
可以看出測試的結果是比較滿意的,改進過的RightPosEx函數效率比RightPos高很多,考慮到測試用的字元串比較短,使用長字元串效果更明顯。如果直接使用Delphi的字元串格式而不用轉為PChar格式,則效率又可提高一步。但字元串格式是Delphi語言自定義的,存在相容性問題。是以這一版本的字串搜尋函數就不列出來了,讀者在前面代碼的基礎上很容易就可寫出來的。代碼優化到底要執行到哪一步,是仁者見仁,智者見智的問題。有的人偏重于提高效率,有的人偏重于保證相容性,這需要在實際使用過程中作取舍。
----
實際測試,
{*
//長字元串(大概1M左右)
1000次
BDS 2007 見Demos
(Delete) time: 323301
(CopyStr) time: 953036
(LastPosEx)Pos: 144881
time: 3665003
(Pos)Pos: 58745
time: 314023
(FastPosEx)Pos: 58745
time: 11383136
(fstAt)Pos: 58745
time: 354948
(SmartPos)Pos: 58745
time: 206409
*}
const
COUNT_NUM = 10000;
procedure TForm1.btnDeleteAndCopyStrClick(Sender: TObject);
i, lvlen, iPos: Integer;
PerformanceCount1, PerformanceCount2, Count: int64;
lvSubString, lvSourceString: string;
lvlen := COUNT_NUM; //重複次數
lvSourceString := edtSourceString.Text;
if chkUseMemoSource.Checked then
lvSourceString := mmoSourceString.Text;
lvlen := lvlen div 10;
QueryPerformanceCounter(PerformanceCount1); //開始計數
lvSubString := lvSourceString;
for i := 0 to lvlen - 1 do
Delete(lvSubString, 1, 1);
QueryPerformanceCounter(PerformanceCount2); //結束計數
Count := (PerformanceCount2 - PerformanceCount1);
mmoMessage.Lines.Add('(Delete) time: ' + inttostr(Count));
lvSubString := CopyStr(lvSubString, 2, Length(lvSubString));
mmoMessage.Lines.Add('(CopyStr) time: ' + inttostr(Count));
procedure TForm1.btnPosTestClick(Sender: TObject);
lvSubString := edtPosTest.Text;
iPos := LastPosEx(lvSubString, lvSourceString); //被測試的函數
mmoMessage.Lines.Add('(LastPosEx)Pos: ' + inttostr(iPos) + sLineBreak + ' time: ' + inttostr(Count));
iPos := Pos(lvSubString, lvSourceString); //被測試的函數
mmoMessage.Lines.Add('(Pos)Pos: ' + inttostr(iPos) + sLineBreak + ' time: ' + inttostr(Count));
iPos := FastPosEx(PChar(lvSourceString), Length(lvSourceString), PChar(lvSubString), Length(lvSubString)); //被測試的函數
mmoMessage.Lines.Add('(FastPosEx)Pos: ' + inttostr(iPos) + sLineBreak + ' time: ' + inttostr(Count));
iPos := fstAt(lvSubString, lvSourceString); //被測試的函數
mmoMessage.Lines.Add('(fstAt)Pos: ' + inttostr(iPos) + sLineBreak + ' time: ' + inttostr(Count));
iPos := SmartPos(lvSubString, lvSourceString); //被測試的函數
mmoMessage.Lines.Add('(SmartPos)Pos: ' + inttostr(iPos) + sLineBreak + ' time: ' + inttostr(Count));