Delphi 應用程式效能分析對於提升軟體效能至關重要,本文將介紹如何透過手動計時和專業工具進行效能分析。手動計時適用於針對特定程式碼區塊進行精確測量,而專業工具則能提供更全面的效能資料。文章將探討 AsmProfiler、Sampling Profiler、AQTime 和 Nexus Quality Suite 等工具的應用,包含設定、使用步驟及結果解讀,並以實際案例說明如何找出效能瓶頸。同時,文章也將比較各工具的優缺點,協助開發者選擇合適的工具進行程式碼效能最佳化。
深入程式碼效能分析
在軟體開發過程中,效能分析(Profiling)是最佳化程式碼、提升系統效能的重要步驟。本文將探討如何透過手動插入計時程式碼和使用專業的效能分析工具(Profilers)來測量和分析程式的執行時間,從而找出效能瓶頸並進行最佳化。
使用計時器進行效能測量
在 SlowCode 程式中,我們首先透過手動插入 TStopwatch 計時器來測量程式不同部分的執行時間。以下是具體步驟:
- 建立計時器:在程式啟動時建立三個計時器,分別用於測量
SlowMethod、ElementInDataDivides和Filter方法的執行時間。
Timing_ElementInData := TStopwatch.Create; Timing_Filter := TStopwatch.Create; Timing_SlowMethod := TStopwatch.Create;
2. **記錄執行時間**:在程式結束時,輸出每個計時器的累積執行時間。
```delphi
Writeln('Total time spent in SlowMethod: ', Timing_SlowMethod.ElapsedMilliseconds, ' ms');
Writeln('Total time spent in ElementInDataDivides: ', Timing_ElementInData.ElapsedMilliseconds, ' ms');
Writeln('Total time spent in Filter: ', Timing_Filter.ElapsedMilliseconds, ' ms');
- 在方法中啟動和停止計時器:在需要測量的方法開始處啟動計時器,在結束處停止計時器。對於包含提前離開的
ElementInDataDivides方法,使用try..finally區塊確保計時器在離開前停止。
function ElementInDataDivides(data: TList
### 詳細測量與分析
透過上述方法,我們獲得了每個方法的總執行時間。然而,為了更精確地瞭解 `ElementInDataDivides` 方法的呼叫來源,我們進一步修改程式碼,區分直接從 `SlowMethod` 和從 `Filter` 方法呼叫 `ElementInDataDivides` 的執行時間。
```delphi
var
Generate_ElementInData_ms: int64;
Filter_ElementInData_ms: int64;
function SlowMethod(highBound: Integer): TArray<Integer>;
var
i: Integer;
temp: TList<Integer>;
begin
Timing_SlowMethod.Start;
temp := TList<Integer>.Create;
try
Timing_ElementInData.Reset;
for i := 2 to highBound do
if not ElementInDataDivides(temp, i) then
temp.Add(i);
Generate_ElementInData_ms := Generate_ElementInData_ms + Timing_ElementInData.ElapsedMilliseconds;
Timing_ElementInData.Reset;
Result := Filter(temp);
Filter_ElementInData_ms := Filter_ElementInData_ms + Timing_ElementInData.ElapsedMilliseconds;
finally
FreeAndNil(temp);
end;
Timing_SlowMethod.Stop;
end;
內容解密:
Generate_ElementInData_ms和Filter_ElementInData_ms用於記錄不同呼叫情境下的執行時間。- 在
SlowMethod中,先重置Timing_ElementInData,並在資料生成階段後記錄其值到Generate_ElementInData_ms。 - 然後再次重置
Timing_ElementInData,並在Filter方法呼叫後記錄其值到Filter_ElementInData_ms。
使用專業的效能分析工具
雖然手動插入計時程式碼可以測量特定部分的執行時間,但對於複雜的程式,使用專業的效能分析工具(Profilers)更加高效。效能分析工具可以測量方法的執行速度、記憶體使用情況、測試覆寫率等多種引數,並提供詳細的呼叫樹狀圖。
效能分析工具主要透過兩種方式工作:
取樣(Sampling):定期檢查程式的執行狀態,統計哪些程式碼行被頻繁執行。取樣分析對程式效能影響小,適合初步分析。
插樁(Instrumentation):修改程式碼或二進位檔案以記錄詳細的執行資訊。插樁分析提供更全面的資料,但可能顯著影響程式效能。
圖表翻譯:
此圖示展示了取樣分析和插樁分析的工作原理及其優缺點對比。
重點整理:
- 手動插入計時程式碼適合測量特定部分的執行時間。
- 專業的效能分析工具提供更全面的效能資料和分析功能。
- 取樣分析和插樁分析各有其適用場景和優缺點。
透過結合上述方法,我們可以更有效地進行程式碼最佳化,提升軟體的效能和使用者經驗。
程式碼效能分析工具詳解
效能分析是最佳化程式碼效能的關鍵步驟。由於工具眾多,選擇合適的分析工具十分重要。本章將介紹四種常見的效能分析工具,包括兩款開源免費工具(AsmProfiler 和 Sampling Profiler)及兩款商業工具(AQTime 和 Nexus Quality Suite),協助讀者選擇最適合的工具。
AsmProfiler 詳解
AsmProfiler 是由 André Mussche 開發的一款 32 位元的插樁式和取樣式效能分析工具。其原始碼和 Windows 可執行檔可在 GitHub 下載。AsmProfiler 同時支援插樁式和取樣式分析,本文將分別介紹這兩種使用方式。
使用 AsmProfiler
- 將 AsmProfiler 的 ZIP 檔案解壓縮至本機資料夾,會產生
Sampling和Instrumenting兩個子資料夾。 - 執行
AsmProfiling_Sampling以啟動取樣式分析工具,點選 Start profiling 開始。
AsmProfiler 提供兩種啟動程式分析的方式:
- 手動附加程式:點選 Select process 並從執行中的程式列表中選擇目標程式。
- 直接執行程式:點選 Select exe 選擇編譯好的 EXE 檔案,並點選 Start process now 直接執行程式,或點選 Start sampling 同時啟動取樣。
設定取樣選項
在 Options 群組中可設定以下引數:
- Sampling interval:設定每次取樣之間的時間間隔(毫秒)。例如,設為 4 會產生每秒 250 次的取樣頻率。
- 設定為 0:啟用連續取樣模式,仍會在取樣間呼叫
Sleep(0)以釋放部分 CPU 資源。 - 設定為 -1:以最快速度進行取樣,AsmProfiler 將佔用一個 CPU 核心。
- 設定為 0:啟用連續取樣模式,仍會在取樣間呼叫
檢視分析結果
完成取樣後,點選 Stop sampling,再點選 Show results 以分析結果。以下為產生結果的步驟:
- 啟動 AsmProfiler。
- 點選 Select exe 並選擇
SlowCode.exe。 - 點選 Start process now。
- 點選 Start sampling。
- 在程式中輸入
100,000。 - 點選 Stop sampling。
- 在程式中輸入
0以離開。 - 點選 Show results。
- 在 Sampling Results 表單中點選 Results。
結果解析
AsmProfiler 以執行緒為單位顯示結果,並自動高亮主執行緒。結果表格包含以下資訊:
- 模組:主要 EXE 或 DLL。
- 函式名稱:函式名稱(經編譯器修飾)。
- Own time:函式自身執行時間(絕對值及百分比)。
- Child time:被呼叫函式的總執行時間。
- Own+Child:函式自身及被呼叫函式的總執行時間。
若依 Own time 排序,會發現 TextIn$qqrr15System.TTextRec(讀取控制檯輸入的函式)佔用較多時間,可忽略。ElementInDataDivides$qqrp40... 是我們關注的函式,它被取樣到 371 次,總耗時約 0.6 秒。在 Detailed View 中可檢視該函式的詳細呼叫資訊及各行程式碼的執行次數。
程式碼名稱修飾解析
Delphi 編譯器對函式名稱進行修飾(Name Mangling),以支援多載函式。例如,SlowMethod(highBound: Integer) 在內部被稱為 SlowMethod$qqri。其中 $qqri 部分描述了函式呼叫約定及引數型別。
詳細名稱解析
q表示使用了fastcall呼叫約定,描述引數如何在暫存器及堆積疊中傳遞。r表示函式有一個或多個暫存器傳遞的引數。i表示該函式具有一個整數型別引數。
效能分析流程圖示
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Delphi程式碼效能分析與工具應用
package "系統架構" {
package "前端層" {
component [使用者介面] as ui
component [API 客戶端] as client
}
package "後端層" {
component [API 服務] as api
component [業務邏輯] as logic
component [資料存取] as dao
}
package "資料層" {
database [主資料庫] as db
database [快取] as cache
}
}
ui --> client : 使用者操作
client --> api : HTTP 請求
api --> logic : 處理邏輯
logic --> dao : 資料操作
dao --> db : 持久化
dao --> cache : 快取
note right of api
RESTful API
或 GraphQL
end note
@enduml圖表翻譯:
此圖表呈現了使用 AsmProfiler 進行效能分析的主要步驟:
- 啟動 AsmProfiler 並選擇目標 EXE 檔案。
- 開始取樣並執行目標程式進行測試。
- 停止取樣並檢視詳細的效能分析結果。
- 結果可依 Own time 排序,以找出效能瓶頸。
- 分析關鍵函式以最佳化程式效能。
重點解析與最佳實踐
- 適當設定取樣頻率:過高的取樣頻率會影響程式效能,而過低的頻率可能遺漏重要資訊。通常設定為每秒數百次是較好的平衡點。
- 關注 Own time 較高的函式:這類別函式通常是效能瓶頸所在,需優先最佳化。
- 使用 Detailed View 分析函式內部行為:透過行級別的執行次數統計,可進一步定位到具體的效能問題程式碼。
- 善用名稱修飾資訊進行除錯:理解 Delphi 編譯器的名稱修飾規則,有助於正確識別多載函式及引數型別。
程式碼分析工具的使用與比較
在軟體開發過程中,效能分析是最佳化程式碼的關鍵步驟。本文將介紹幾種Delphi程式碼分析工具的使用方法及其特點,包括AsmProfiler、Sampling Profiler和AQTime。
AsmProfiler的使用
AsmProfiler是一款功能強大的程式碼分析工具,支援多種分析模式。首先,您需要將AsmProfiler.dll複製到Windows環境路徑或執行檔所在的資料夾中。同時,也需要將_uAsmProfDllLoader.pas複製到Delphi的函式庫路徑或專案資料夾中,並將其加入專案中。
設定與初始化
在程式碼中,您需要呼叫以下函式來初始化AsmProfiler:
if _uAsmProfDllLoader.LoadProfilerDll then
_uAsmProfDllLoader.ShowProfileForm;
開始與停止分析
您可以在程式碼中控制分析的開始與停止:
_uAsmProfDllLoader.StartProfiler(False);
_uAsmProfDllLoader.StopProfiler;
編譯器選項設定
使用AsmProfiler時,需要正確設定編譯器選項:
- 連結器:Map檔案 = 詳細資訊
- 編譯器:最佳化 = 關閉
- 編譯器:堆積疊框架 = 開啟
對於Delphi 11,還需要關閉以下連結器選項:
- 資料執行保護相容 = 關閉
- 支援位址空間組態隨機化(ASLR) = 關閉
實際操作範例
在SlowCode_VCL專案中,我們將分析程式碼放在btnTestClick事件處理函式中:
procedure TfrmSlowCode.btnTestClick(Sender: TObject);
var
data: TArray<Integer>;
begin
outResults.Text := '';
outResults.Update;
_uAsmProfDllLoader.StartProfiler(False);
data := SlowMethod(inpHowMany.Value);
_uAsmProfDllLoader.StopProfiler;
ShowElements(data);
end;
分析結果
執行程式後,點選Test按鈕,即可進行分析。分析結果將顯示在Unit overview標籤頁中,提供詳細的時間資訊和呼叫樹狀結構。
內容解密:
_uAsmProfDllLoader.LoadProfilerDll:載入AsmProfiler的DLL檔案。_uAsmProfDllLoader.ShowProfileForm:顯示AsmProfiler的主表單。_uAsmProfDllLoader.StartProfiler(False):開始分析程式碼,引數False表示不清除之前的分析結果。_uAsmProfDllLoader.StopProfiler:停止分析程式碼。
Sampling Profiler的使用
Sampling Profiler是一款由Eric Grange開發的抽樣式分析工具,支援多執行緒分析,並可透過OutputDebugString命令控制分析的開啟與關閉。
主要特點
- 可設定哪些CPU執行分析器,哪些CPU執行被分析的應用程式。
- 可透過
OutputDebugString('SAMPLING THREAD threadID')命令指定要分析的執行緒。 - 可啟用內建的網頁伺服器,透過瀏覽器即時檢視分析結果。
使用限制
Sampling Profiler無法選擇要分析的方法,所有方法的分析結果都會被顯示出來,這可能會造成幹擾。
AQTime的使用
AQTime是一款由SmartBear Software開發的效能和記憶體分析工具,支援多種程式語言和平台。
主要特點
- 支援32位元和64位元應用程式。
- 提供詳細的效能和記憶體分析結果。
使用限制
AQTime是一款商業軟體,需要購買授權才能使用。
程式碼效能分析工具的介紹與使用
在軟體開發過程中,程式碼的效能分析是至關重要的一環。良好的效能分析工具能夠幫助開發者找出程式中的效能瓶頸,從而進行最佳化。本文將介紹兩款用於Delphi程式開發的效能分析工具:AQTime Professional和Nexus Quality Suite。
AQTime Professional
AQTime Professional是一款功能強大的效能分析工具,支援從Delphi 2006到10.4 Sydney的所有版本,甚至可以在Visual Studio中使用。它提供了多種效能分析器,包括效能分析器(Performance Profiler)、取樣分析器(Sampling Profiler)、覆寫率分析器(Coverage Profiler)等。
使用AQTime Professional進行效能分析
- 準備工作:確保編譯器選項中Stack frames、Debug information和Local symbols均已啟用,並且連結器的Debug information也已啟用。
- 設定搜尋路徑:在AQTime的選項中,新增所有包含原始碼的資料夾。
- 選擇要分析的方法:可以選擇特定的單元或方法進行分析,以避免過多的效能開銷。
- 建立新的效能分析區域:選擇要分析的方法,然後右鍵點選並選擇「Add selected to | New profiling area」。
- 執行程式並收集資料:從AQTime啟動程式,執行想要分析的操作,然後離開。AQTime將顯示效能分析結果。
AQTime Professional的特點
- 提供詳細的呼叫圖(Call Graph)和編輯器面板,顯示原始碼及每行的執行次數。
- 可以作為獨立應用程式使用,也可以整合到Delphi IDE中。
Nexus Quality Suite
Nexus Quality Suite(NQS)是另一款支援Delphi 5到11.3 Alexandria版本的效能分析工具。它提供了多種效能分析器,包括Method Timer、Line Timer和Block Timer等。
使用Nexus Quality Suite進行效能分析
- 安裝與整合:NQS整合到Delphi的Tools選單中,提供了多種效能分析工具。
- Method Timer和Line Timer的使用:這兩款工具分別用於方法級別和行級別的效能分析。只需啟用除錯資訊,然後選擇要分析的方法,最後執行程式並檢視結果。
Nexus Quality Suite的特點
- 提供了多種效能分析工具,如Method Timer和Line Timer。
- Line Timer提供了按行號分組的結果顯示,能夠快速定位到最關鍵的程式碼部分。
程式碼範例與詳細註解
以下是一個簡單的Delphi程式碼範例,用於示範如何使用效能分析工具:
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
sum: Int64;
begin
sum := 0;
for i := 1 to 100000000 do
sum := sum + i;
ShowMessage(IntToStr(sum));
end;
內容解密:
- 迴圈運算:上述程式碼在按鈕點選事件中執行了一個大迴圈,用於計算1到1億的整數之和。
- 效能瓶頸:這個迴圈可能是效能瓶頸,因為它執行了大量的運算。
- 最佳化建議:可以使用數學公式直接計算1到n的和,避免使用迴圈,從而大幅提升效能。