<a>動态優化編譯器</a>
我們希望 Java 應用程式的計算部分隻涉及 Java 源代碼的一小部分。Jalape�o 的優化編譯器緻力于高效地編譯這些位元組碼。優化編譯器是 動态的:它在應用程式運作時編譯方法。将來,優化編譯器也将是 自适應的:它将在計算密集的方法上被自動調用。優化編譯器的目标是在給定的編譯時間預算内生成所標明方法的盡可能好的代碼。此外,它的優化必須在正确地保護異常、垃圾回收和線程的 Java 語義的同時很大地提高性能。對實作 SMP 伺服器的可伸縮性性能來說,降低同步和其它線程原語的花費尤其重要。最後,以最少的工作量把優化編譯器的目标重定向到各種硬體平台應是可能的。建構達到這些目标的動态優化編譯器是一個很大的挑戰。
從位元組碼到中間表示(intermediate representation)
副本傳播是在轉換期間執行快速優化的一個示例。Java 位元組碼常常包含執行計算并把結果存儲到本地變量的指令序列。對中間表示的生成的一個天真想法是為計算結果建立一個臨時寄存器,并另外用一條指令把這個寄存器的值移入本地變量。簡單的副本傳播試探法清除了大量這些不必要的臨時寄存器。當從臨時寄存器把值存儲到本地變量時,将檢測到最近生成的指令。如果是這條指令建立了這個臨時寄存器來存儲結果,那麼這條指令就被改成把結果直接寫到本地變量。
為盡量減少為同塊位元組碼生成 HIR 的次數,簡單貪婪算法為抽象解釋選擇有最低開始位元組碼索引的塊。這個簡單的試探法依賴于這樣的事實,即除了循環,所有控制流構造都是以拓撲有序方式生成的,而且控制流圖是可簡化的。偶而地,這個試探法看來獲得了用目前 Java 源代碼編譯器編譯的方法的擴充基礎塊的最優順序。
進階優化
HIR 中的指令很好地模仿了 Java 位元組碼,有兩個重要不同 ― HIR 指令在符号寄存器操作數上進行操作,而不是在隐式堆棧上,而且 HIR 包含獨立的操作符,用于實作運作時異常的顯式檢查(例如,數組邊界檢查)。相同的運作時檢查通常需要多于一條的指令。(例如,incrementing A[ i] 可能涉及兩個獨立的數組通路,但隻需一次邊界檢查。)對這些檢查指令的優化減少了執行時間并使另外的優化更容易。
目前,HIR 使用帶有适度編譯時開銷的簡單的優化算法。這些優化有三類:
本地優化。這些優化對擴充基礎塊是本地的,例如,公共子表達式清除、備援異常檢查清除以及備援裝入清除。
這種技術能捕捉到很多優化機會,但其它的情況隻能由流敏感的算法檢測到。
方法調用的内聯擴充。為在 HIR 級别上把方法調用擴充成内聯,被調用方法的 HIR 被生成并被補入到調用者的 HIR。靜态的、基于大小的試探法目前用于控制對靜态和最後方法的調用的自動内聯擴充。對于非最後虛方法調用,優化編譯器預測虛調用的接收方為對象的聲明類型。它用運作時測試來監視每個内聯虛方法以驗證對接收方的預測是正确的,如果不正确,則預設設定為正常虛方法調用。在有動态類裝入的情況下,這種運作時測試是安全的。
由于 Jalape�o 是用 Java 寫的,與用于把應用程式方法擴充為内聯的架構相同的架構也可用于把對運作時方法的調用擴充為内聯(特别是同步和對象配置設定)。一般說來,從應用程式代碼到 Java 庫,下至 Jalape�o 運作時系統,都可以把調用擴充為内聯,這為優化提供了極好的機會。
低級優化
在執行了進階分析和優化之後,HIR 被轉換為 低級中間表示(low-level intermediate representation(LIR))。LIR 把 HIR 指令擴充為特定于 Jalape�o 虛拟機的對象布局和參數傳遞約定的操作。例如,虛方法調用被表達為類似于 invokevirtual 位元組碼的單條 HIR 指令。這一條 HIR 指令被轉換在三條 LIR 指令,分别負責從一個對象獲得 TIB 指針,從 TIB 獲得适當方法體的位址,以及将控制轉到方法體。
由于字段和頭的偏移量現在都是可用的常數,新的優化機會出現了。原則上,任何進階優化也可用在 LIR 上。然而,由于 LIR 的大小可能是相應 HIR 大小的兩到三倍,是以在進行 LIR 優化時要更留意編譯時間開銷。目前,清除本地的公共子表達式是 LIR 上進行的唯一優化。由于 HIR 和 LIR 共享相同的基礎設施,是以在 HIR 上執行公共子表達式清除的代碼不用修改就可重用在 LIR 上。
指令選擇和特定于機器的優化
BURS 是代碼生成器(code-generator)生成器,類似于掃描器和分析器生成器。想得到的目标體系結構的指令選擇由 樹文法(tree grammar)指定。樹文法中的每一條規則都有一個相關花費(反映生成的指令的大小和指令的預期周期數)和代碼生成動作。處理樹文法以生成一組表,這些表在編譯時驅動指令選擇。
在指令選擇上使用 BURS 技術有兩個重要好處。首先,編譯時進行的樹型比對法通過使用動态程式設計為所有輸入樹找到了最少花費的分析(與樹文法中指定的花費相比)。其次,建構 BURS 基礎設施的花費可在幾個目标體系結構中分期付清。特定于體系結構的部分相對較少;Jalape�o 的 PowerPC 樹文法約有 300 條規則。
方法序言配置設定一個堆棧架構,儲存方法需要的任何非易失性寄存器,并且檢查是否有人提出讓出請求。結語恢複任何被儲存的寄存器并解除堆棧幀配置設定。如果方法被同步,則序言鎖定,而且結語解鎖,指定對象也被同步。
優化編譯器然後把可執行的二進制代碼放到指令數組,即方法體。通過把中間指令偏移量轉換為機器代碼偏移量,這個裝配階段也最後确定了異常表和指令數組的引用映射圖。
優化的級别
優化編譯器可在不同的優化級别上執行。每個級别都包含前一級别的所有優化和一些其它東西。 級别 1恰好包含上面描述的優化。(存在 級别 0 主要是出于調試的目的,它與級别 1 相似,但沒有任何進階或低級優化。)兩個級别的更加激烈的優化也在計劃之中。
操作的形态。
優化編譯器的運作方式是想作為自适應 JVM 的一個元件。圖 5 顯示了這樣一個虛拟機的整體設計。優化編譯器是 Jalape�o 的自适應優化系統的關鍵組成部分,這個自适應系統也包含聯機測量(on-line measurement)和正在開發的控制器子系統(controller subsystem)。通過使用軟體采樣和成型技術和來自硬體性能螢幕的資訊的概要,聯機測量子系統将監視單個方法的性能。當聯機測量子系統檢測到某個性能門檻值被達到時,控制器子系統将被調用。控制器将用概要資訊建構一個“優化計劃”,這個計劃描述了哪個方法應被編譯以及應用哪一個優化級别。然後調用優化編譯器來編譯優化計劃中的方法。聯機測量子系統繼續監視單個方法,包括那些已經優化的方法,以在必要時觸發進一步的優化過程。
優化編譯器也可用作 JIT 編譯器,在方法第一次執行時編譯所有方法。當要設定優化編譯器的性能基準時,優化編譯器同時用作靜态引導映象編譯器(針對引導映象的 JVM 代碼)和 JIT 編譯器(針對基準程式代碼和任何餘下的 JVM 代碼)。