軟體工程/工具/效能分析簡介
在軟體工程中,程式效能分析、軟體效能分析或簡稱效能分析,是一種動態程式分析(與靜態程式碼分析相對),它透過收集程式執行期間的資訊來調查程式的行為。這種分析的通常目的是確定程式的哪些部分需要最佳化——以提高其整體速度、減少其記憶體需求,或者有時兩者兼而有之。
- (程式碼)效能分析器是一種效能分析工具,最常見的是僅測量函式呼叫的頻率和持續時間,但除了更全面的效能分析器之外,還有其他特定型別的效能分析器(例如記憶體效能分析器),能夠收集廣泛的效能資料。
- 指令集模擬器——也必然是一個性能分析器——可以測量程式從呼叫到終止的全部行為。
效能分析器使用各種技術來收集資料,包括硬體中斷、程式碼插樁、指令集模擬、作業系統鉤子和效能計數器。效能分析器的使用在效能工程過程中被“呼叫”。
程式分析工具對於理解程式行為極其重要。計算機架構師需要這樣的工具來評估程式在新架構上的效能。軟體編寫人員需要工具來分析他們的程式並識別程式碼的關鍵部分。編譯器編寫人員經常使用此類工具來了解他們的指令排程或分支預測演算法的執行效果……(ATOM,PLDI,'94)
效能分析器的輸出可能為:
- 觀察到的事件的統計摘要(概要)
- 摘要概要資訊通常顯示在事件發生位置的原始碼語句上,因此測量資料的大小與程式的程式碼大小成線性關係。
/* ------------ source------------------------- count */ 0001 IF X = "A" 0055 0002 THEN DO 0003 ADD 1 to XCOUNT 0032 0004 ELSE 0005 IF X = "B" 0055
- 記錄事件的流(跟蹤)
- 對於順序程式,通常使用摘要概要就足夠了,但並行程式中的效能問題(等待訊息或同步問題)通常取決於事件的時間關係,因此需要完整的跟蹤才能瞭解正在發生的事情。
- (完整)跟蹤的大小與程式的指令路徑長度成線性關係,使其在某種程度上不切實際。因此,可以啟動程式中的一個點,並在另一個點終止跟蹤以限制輸出。
- 與虛擬機器的持續互動(例如透過螢幕顯示進行連續或週期性監控)
- 這提供了在執行期間任何所需點切換跟蹤開/關的機會,以及檢視有關(仍在執行的)程式的正在進行的指標。它還提供了在關鍵點暫停非同步程序以更詳細地檢查與其他並行程序互動的機會。
效能分析工具從 20 世紀 70 年代初就在 IBM/360 和 IBM/370 平臺上存在,通常基於定時器中斷,這些中斷在設定的定時器間隔記錄程式狀態字 (PSW) 以檢測執行程式碼中的“熱點”。這是早期取樣(見下文)的例子。1974 年初,指令集模擬器允許進行完整的跟蹤和其他效能監控功能。
Unix 上的效能分析器驅動的程式分析至少可以追溯到 1979 年,當時 Unix 系統包含一個基本工具“prof”,該工具列出每個函式及其使用的程式執行時間的多少。1982 年,gprof 將此概念擴充套件到完整的呼叫圖分析[1]
1994 年,數字裝置公司的 Amitabh Srivastava 和 Alan Eustace 發表了一篇論文,描述了 ATOM。[2] ATOM 是一個將程式轉換為其自身效能分析器的平臺。也就是說,在編譯時,它將程式碼插入到要分析的程式中。插入的程式碼輸出分析資料。這種修改程式以分析自身的技術被稱為“插樁”。
2004 年,gprof 和 ATOM 論文都出現在有史以來 50 篇最有影響力的 PLDI 論文列表中。[3]
扁平效能分析器根據呼叫計算平均呼叫時間,並且不根據被呼叫方或上下文分解呼叫時間。
呼叫圖效能分析器顯示函式的呼叫時間和頻率,以及基於被呼叫方的呼叫鏈。但是上下文不會被保留。
此處列出的程式語言具有基於事件的效能分析器
- Java:JVMTI(JVM 工具介面)API(以前稱為 JVMPI(JVM 效能分析介面))為效能分析器提供掛鉤,用於捕獲諸如呼叫、類載入、解除安裝、執行緒進入離開等事件。
- .NET:可以將效能分析代理作為 COM 伺服器附加到 CLR。與 Java 類似,執行時隨後會向代理提供各種回撥,用於捕獲諸如方法 JIT/進入/離開、物件建立等事件。特別強大之處在於效能分析代理可以以任意方式重寫目標應用程式的位元組碼。
- Python:Python 效能分析包括 profile 模組、hotshot(基於呼叫圖)以及使用 'sys.setprofile' 函式來捕獲諸如 c_{call,return,exception}、python_{call,return,exception} 等事件。
- Ruby:Ruby 也使用類似於 Python 的介面進行效能分析。存在 profile.rb 模組中的扁平效能分析器和 ruby-prof C 擴充套件。
一些效能分析器透過取樣來執行。取樣效能分析器使用作業系統中斷定期探測目標程式的程式計數器。取樣概要通常在數值上不太準確和具體,但允許目標程式以接近全速執行。
得到的資料不是精確的,而是統計近似值。實際的誤差通常大於一個取樣週期。事實上,如果一個值是取樣週期的n倍,則其預期誤差為n個取樣週期的平方根。 [4]
在實踐中,取樣分析器通常可以比其他方法提供目標程式執行的更準確的影像,因為它們對目標程式的侵入性較小,因此不會產生太多副作用(例如記憶體快取或指令解碼流水線上的副作用)。此外,由於它們對執行速度的影響較小,因此可以檢測到其他情況下會被隱藏的問題。它們也相對不容易高估小型、頻繁呼叫的例程或“緊湊”迴圈的成本。它們可以顯示在使用者模式下花費的時間與可中斷核心模式下(例如系統呼叫處理)花費的時間的相對數量。
儘管如此,處理中斷的核心程式碼會導致輕微的 CPU 週期損失,轉移快取使用,並且無法區分不可中斷核心程式碼中發生的各種任務(微秒級活動)。
專用硬體可以超越這一點:一些最近的 MIPS 處理器 JTAG 介面有一個 PCSAMPLE 暫存器,它以真正無法檢測的方式對程式計數器進行取樣。
一些最常用的統計分析器包括 AMD CodeAnalyst、Apple Inc. Shark、gprof、Intel VTune 和 Parallel Amplifier(Intel Parallel Studio 的一部分)。
一些分析器使用附加指令來檢測目標程式以收集所需的資訊。
檢測程式可能會導致程式效能發生變化,從而可能導致結果不準確和海森堡錯誤。檢測始終會對程式執行產生一定影響,通常會使其變慢。但是,檢測可以非常具體,並且可以仔細控制以使其影響最小。對特定程式的影響取決於檢測點的放置和用於捕獲跟蹤的機制。硬體對跟蹤捕獲的支援意味著在某些目標上,檢測可以在單個機器指令上進行。檢測的影響通常可以從結果中推匯出(即透過減法消除)。
gprof 是一個使用檢測和取樣的分析器示例。檢測用於收集呼叫者資訊,而實際的時間值是透過統計取樣獲得的。
- 手動:由程式設計師執行,例如透過新增指令來顯式計算執行時間,簡單地計數事件或呼叫測量 API(例如應用程式響應測量標準)。
- 自動原始碼級別:根據檢測策略,由自動工具向原始碼新增檢測。
- 編譯器輔助:例如:gprof 的“gcc -pg …”、Quantify 的“quantify g++ …”
- 二進位制翻譯:工具向已編譯的二進位制檔案中新增檢測。例如:ATOM
- 執行時檢測:在執行之前,程式碼會被檢測。程式執行完全由工具監督和控制。例如:Pin、Valgrind
- 執行時注入:比執行時檢測更輕量級。在執行時修改程式碼以跳轉到輔助函式。例如:DynInst
- 直譯器除錯選項可以在直譯器遇到每個目標語句時啟用效能指標的收集。位元組碼、控制表或 JIT 直譯器是三個示例,它們通常對目的碼的執行具有完全控制權,從而能夠提供極其全面的資料收集機會。
- 虛擬機器:透過在虛擬機器下執行(通常)未修改的程式來收集資料。例如:SIMMON
- 模擬器和虛擬機器:透過在指令集模擬器下執行未修改的程式以互動方式和選擇性地收集資料。例如:SIMON(批處理互動式測試/除錯)和 IBM OLIVER(CICS 互動式測試/除錯)。
- Dunlavey,“Performance tuning with instruction-level cost derived from call-stack sampling”,ACM SIGPLAN Notices 42, 8(2007 年 8 月),第 4-8 頁。
- Dunlavey,“Performance Tuning: Slugging It Out!”,Dr. Dobb's Journal,第 18 卷,第 12 期,1993 年 11 月,第 18-26 頁。
- 文章“速度至上——消除效能瓶頸”介紹瞭如何使用 IBM Rational Application Developer 對 Java 應用程式進行執行時間分析。
- 使用 VTune™ Performance Analyzer 對執行時生成和解釋的程式碼進行分析