延續筆者去年的紀錄「當 Compiler 遇上 Mobile」,最近我們又獲得一些進展,是在 Android 的 Dalvik 虛擬機器環境中,引入 pre-compiled class 的實驗,除了可改善執行時期的效能、記憶體使用量外,另外就是避免在「反組譯並修改 Android 應用程式實例」一文可見的資訊保護議題。
其實十幾年前,在 Java 平台中就有相當多團隊提出可行的方案,而世界上第一個開放原始碼的 Kaffe 虛擬機器專案,早在 1999 年即提出實做 "Kaffe/GCJ integration",成功地將 GCJ (GCC for Java) 自 Java class/source 編譯得到的機械碼,當作 pre-compiled shared library,讓 Kaffee VM 讀取,預期可有效改善執行效能並縮減起始時間。GNU Classpath 專案主持人 Mark Wielaard 在 LWN 的文章 "GCJ - past, present, and future" 提到相關的歷史與技術背景,最早可回溯到 Cygnus solutions 時期 (RedHat 尚未併購) 的 GCJ 計畫提案 -- "A Gcc-based Java Implementation"。
為了降低實做的複雜度,筆者最早採用 LLVM 與 IcedTea 作為系統框架,不過一直到今年春節,整體進度還是陷入膠著的狀態。現在則引入 Java2C 搭配 PGO (Profile-Guided Optimization) 與統計模型,嘗試分析運行於 Dalvik VM 的 Android activity / system server,佔用系統資源最頻繁的項目,並預先編譯 (pre-compile) 這些 class/method,居中透過 JNI (Java Native Interface) 讓 Dalvik VM 存取。不過,實務上來說,不是將所有 class/method 都編譯為原生機械碼,就會帶來效能提昇,相反地,後者往往讓系統陷入更差的效能。目前 Eclair 裡面的 Dalvik VM 雖然沒有完整的 JIT compiler,但其 fast interpreter for ARM 的確做了頗多 threaded interpreter 的改進,考量到 I-cache, paging, 與 branch prediction,若我們不思量 Java / Android Dalvik 應用程式的執行時期行為,只是一味作編譯轉換,很可能只是編譯極少被執行,或者完全不會被執行到的程式碼,因此加重執行時期的負擔,而最該被優化的部份,受限於不完整的 source-to-source 轉換,有顧此失彼的疑慮。
於是,找出系統中最該被優化的部份,並考量到正確性與相容度,就是我們的首要研究議題,筆者採用知名的 SciMark 2.0 作為量化分析的指標。以下是在 Qualcomm MSM7x25 硬體平台 (arm1136j-s) 的效能評比,ARM11 的時脈是 528 MHz,ARM Linux 版本為 2.6.29.6-0xlab:由上可見,在 SciMark 2.0 的各項評比中,Pre-compiled class 執行效能都較 Android Dalvik fast interpreter for ARM 給予一定程度的改良,並且透過 FDO (Feedback Directed Optimization),大多可進一步給予提昇。這僅是初步的整合實驗數據,還需要更多分析與進行實做。
在 Android/Dalvik 環境引入 precompiled class 的實驗
對 String 操作的改進
之前的文章「Android Dalvik VM vs. Java VM」提及純 interpreter 的 Dalvik VM 與具備 Just-In-Time compiler 的 JBend JVM (基於 CVM 引擎) 的效能比較,以修改過的 Embedded CaffeineMark 作為量化的依據,可發現 String 測試項目差了一個數量級 (491 vs. 5109),整體的分數是 1:4.18 (1811:433)。稍早 ansoncat 做了初步分析:
「這部分應該是由於在 CVM 中,目前 0xdroid 在不更動 Dalvik VM 實做的前提 (後續再談改進的途徑),對 bionic libc 的 string 相關函式作調整,移植來自 CodeSourcery/ARM 的 ARMv7 Thumb2 優化實做,就 micro-benchmark 的角度來說,效能改進約有 25% (初步分析,要考慮到不同的環境配置),但整合到 Android/Dalvik 的表現又是如何呢?筆者的實驗結果如下: (score 大者為佳)使用了 CNI 來實作 String class 的重要操作 ... 因為 String 的分數拉高了平均, 其它 benchmark 的效能差異會比平均分數的差異大的多」
測試環境是 Beagleboard (500MHz) 運作 0xdroid,以 ARMv7 GNU Toolchain 編譯,"armv7" 是 bionic 更動前的數據,而 "armv7-opt" 則是更動後的表現。很顯然發現,String 一項的改進大幅受到壓抑 (score = 1898:2140),致使兩者最終僅有些微的落差,可見 JNI 帶來 overhead 的著實不小。又,大部分應用程式的效能瓶頸往往在於圖形處理、系統資源的調度、檔案讀寫等 I/O 操作,所以現階段我們的想法是儘量調整 native 的部份,針對 ARM core 與 SoC 的特性作調整,並提出可行的輕量級 native method invocation 實做。 read on
Android Dalvik VM vs. Java VM
Google Android 開發團隊為了技術自主、迴避 Java 商標爭議等考量,建構了嶄新的 Dalvik Virtual Machine,骨子裡頭仍支援 Java 程式語言,但 Dalvik VM 設計稍異於典型 JVM,網路上已有豐富的比較,本文就不贅述,筆者想探討的,則是日本 eFlow 公司最近提出的 'Android™'s Dalvik VM on "Mobile Java"',目標就是讓原本的 Java 執行平台 (主要是 Java ME),得以運作針對 Android Dalvik VM 設計的應用程式。日前該專案已開放原始程式碼,主體依據 GNU GPLv2,以下是相關資訊:
- 新聞稿: eflow Releases First Implementation of Android™'s Dalvik VM on "Mobile Java" as Open Source
- 專案開發主頁: android-dalvik-vm-on-java : Pure Java implementaion of Android's Dalvik virtual machine
- Dalvik Execution file format (.dex)
- Complete Dalvik instruction set
- J2ME CLDC API
- Multi-thread (include synchronized block, wait and notify)
"We believe this implementation will particularly benefit manufacturers currently developing Android-powered devices, with a number of which we are cooperating to port the Dalvik VM to their specific hardware and software architectures. By making this implementation available today as open source, and by accelerating our porting and development efforts, we have the firm intention to make a significant contribution to the broader Android community."這裡無從得知,不過,該專案四月底時,一併將 dalvikvm_benchmark 釋出,伴隨內部的效能分析數據,非常有意思。從裡頭的資料來看,標的硬體平台有兩個:
- Google Dev Phone 1 (528MHz - Qualcomm MSM7201A)
- P905i (500MHz - Panasonic UniPhier 4M + JBlend)
- Google Dev Phone 1 (528MHz - Qualcomm MSM7201A) :: 433
- P905i (500MHz - Panasonic UniPhier 4M + JBlend) :: 1811