離線瀏覽器開發中需大量解析 HTML,字串搜尋是 CPU 熱點。標準 Pos 僅支援首次匹配、區分大小寫,無法滿足「不分大小寫」「找最後一次出現」等需求。本文展示從朴素實作到指標掃描的優化全過程,實測在 Duron 700/256MB 環境下,優化版比原版快 35%,且程式碼更簡潔。
GetTickCount:毫秒級,精度不足。
var C1, C2, Freq: Int64;
QueryPerformanceFrequency(Freq);
QueryPerformanceCounter(C1);
// 執行待測代碼 10000 次
QueryPerformanceCounter(C2);
ShowMessage('Ticks: ' + IntToStr(C2 - C1) + ' Freq: ' + IntToStr(Freq));
function RightPos(const SubStr, S: String): Integer;
var iPos: Integer; Tmp: String;
begin
Tmp := S; Result := 0;
iPos := Pos(SubStr, Tmp);
while iPos <> 0 do begin
Delete(Tmp, 1, iPos + Length(SubStr) - 1); // 每次重新分配記憶體、移動資料
Result := Result + iPos;
iPos := Pos(SubStr, Tmp);
if iPos = 0 then Break;
Result := Result + Length(SubStr) - 1;
end;
end;
致命傷:Delete 內部呼叫 Move + _LStrSetLength,涉及記憶體重新分配與大量資料搬移,CPU 開銷極大。
function RightPosEx(const SubStr, S: String): Integer;
var
I, J, L: Integer;
PStr, PSub: PChar;
begin
PStr := PChar(S); // 轉 PChar 獲取指標
PSub := PChar(SubStr);
Result := 0;
L := Length(SubStr);
for I := 0 to Length(S) - 1 do begin
for J := 0 to L - 1 do
if PStr[I + J] <> PSub[J] then Break;
if J = L then Result := I + 1; // 記錄最後匹配位置 (1-based)
end;
end;
UpCase 或用 CharLowerBuff。| 函數 | QueryPerformanceCounter Ticks | 相對耗時 |
|---|---|---|
| Pos (系統) | 150,000 | 基準 1.00 |
| RightPos (刪除法) | 217,000 | 1.45× |
| RightPosEx (指標法) | 160,000 | 1.07× |
指標法僅比系統優化過的 Pos 慢 7%,但功能更強(找最後、可擴充不分大小寫);刪除法則慢 45%。
「效能優化到哪裡為止,取決於熱點比例與維護成本。」
若該函數佔總 CPU < 5%,進一步優化投資報酬率極低;若為核心解析引擎佔 50%+,則值得引入複雜演算法。
本案例展示:避開高階封裝的記憶體操作(Delete、Pos 內部 Move),改用底層指標線性掃描,即可在不犧牲可讀性前提下大幅提升效能,是 Delphi 字串處理優化的通用範式。