Android runtime層是如何通過縮減代碼來縮減內存的

文章目錄

  • 前言:Android 在設備上改進內存的秘密
  • 優化編譯器101
  • 代碼大小改進
  • 消除寫入障礙
  • 隱式暫停檢查
  • 合并回調
  • 其他優化改進
  • 代碼下沉
  • 循環優化
  • 消除死代碼 – SimplifyAlwaysThrows
  • 加載存儲消除 – 使用 try catch 塊
  • 加載存儲消除 – 使用釋放/獲取操作
  • 新的內聯啟發式
  • 不斷折疊
  • 把它們放在一起
  • 進一步閱讀

前言:Android 在設備上改進內存的秘密

請添加圖片描述
Android 運行時 (ART) 執行由 Java 或 Kotlin 語言編寫的應用程序和系統服務生成的Dalvik字節碼。我們不斷改進 ART 以生成更小、性能更高的代碼。改進 ART 可以使系統和用戶體驗整體上更好,因為它是 Android 應用程序的共同點。在這篇博文中,我們將討論在不影響性能的情況下減少代碼大小的優化

代碼大小是我們關注的關鍵指標之一,因為生成的文件越小,對內存(RAM 和存儲)越有利。通過新版本的 ART,我們估計每臺設備可為用戶節省約 50-100MB 的空間。這可能正是您更新您喜愛的應用程序或下載新應用程序所需要的。由于 ART 可從 Android 12 開始更新,這些優化適用于1B+ 設備,谷歌在全球范圍內為這些設備節省了47-95 PB(47-95 百萬 GB!)

本博文中提到的所有改進都是開源的。它們可以通過 ART 主線更新獲得,因此您甚至不需要完整的操作系統更新即可獲得好處。我們可以把蛋糕倒過來吃!

優化編譯器101

ART使用設備上的 dex2oat 工具將應用程序從DEX 格式編譯為本機代碼。第一步是解析 DEX 代碼并生成中間表示(IR)。使用 IR,dex2oat 執行許多代碼優化。管道的最后一步是代碼生成階段,其中 dex2oat 將 IR 轉換為本機代碼(例如,AArch64 匯編)。

優化管道具有執行的階段,以便每個階段專注于一組特定的優化。例如,常量折疊是一種優化,嘗試用常量值替換指令,例如將加法運算2 + 3折疊為5。
請添加圖片描述

IR 可以打印和可視化,但與 Kotlin 語言代碼相比非常冗長。出于本博文的目的,我們將展示使用 Kotlin 語言代碼進行的優化,但要知道它們正在發生在 IR 代碼上。

代碼大小改進

對于所有代碼大小優化,我們對 Google Play 商店中超過 50 萬個 APK 進行了測試,并匯總了結果。

消除寫入障礙

我們有一個新的優化過程,稱為“寫入障礙消除”。寫屏障會跟蹤自垃圾收集器 (GC) 上次檢查以來已修改的對象,以便 GC 可以重新訪問它們。例如,如果我們有:
請添加圖片描述
以前,我們會為每個對象修改發出一個寫屏障,但我們只需要一個寫屏障,因為:1)標記將在o本身中設置(而不是在內部對象中),2)垃圾收集不能與這些集合之間的線程進行了交互。

如果指令可能觸發 GC(例如 Invokes 和 SuspendChecks),我們將無法消除寫入障礙。在下面的示例中,我們不能保證 GC 不需要檢查或修改修改之間的跟蹤信息:
請添加圖片描述
實施這一新通道有助于減少 0.8% 的代碼大小

隱式暫停檢查

假設我們有幾個線程正在運行。掛起檢查是安全點(由下圖中的房屋表示),我們可以在其中暫停線程執行。使用安全點的原因有很多,其中最重要的是垃圾收集。當發出安全點調用時,線程必須進入安全點并被阻塞,直到它們被釋放。

以前的實現是顯式布爾檢查。我們將加載該值,對其進行測試,并在需要時分支到安全點。
請添加圖片描述
隱式掛起檢查是一種優化,消除了對測試和分支指令的需要。相反,我們只有一個負載:如果線程需要掛起,該負載將捕獲,并且信號處理程序會將代碼重定向到掛起檢查處理程序,就像該方法進行了調用一樣。

請添加圖片描述
更詳細地說,保留寄存器 rX 預加載了線程內的一個地址,其中我們有一個指向其自身的指針。只要我們不需要進行掛起檢查,我們就保留該自指向指針。當我們需要進行掛起檢查時,我們清除指針,一旦它對線程可見,第一個LDR rX, [rX]將加載 null,第二個將出現段錯誤。

掛起請求本質上是要求線程很快掛起一段時間,因此等待第二次加載的輕微延遲是可以接受的。

此優化將代碼大小減少了 1.8%

合并回調

已編譯方法通常具有入口框架。如果它們擁有它,這些方法必須在返回時解構它,這也稱為退出框架。如果一個方法有多個返回指令,它將生成多個退出幀,每個返回指令一個。

通過將返回指令合并為一條,我們能夠擁有一個返回點并能夠刪除多余的退出幀。這對于具有多個 return 語句的 switch 情況特別有用。
請添加圖片描述
合并返回可將代碼大小減少 1%。

其他優化改進

我們改進了許多現有的優化過程。在這篇博文中,我們將它們分組在同一部分,但它們彼此獨立。以下部分中的所有優化都有助于減少 5.7% 的代碼大小。

代碼下沉

代碼下沉是一種優化過程,它將指令下推到不常見的分支,例如以 throw 結尾的路徑。這樣做是為了減少可能不會使用的指令上浪費的周期。

我們通過 try catch 改進了圖中的代碼下沉:現在,只要我們不將代碼下沉到與它開始的嘗試不同的嘗試中(或者如果它不在開始的嘗試中,則在任何嘗試中),我們現在允許下沉代碼和)。
請添加圖片描述

在第一個示例中,我們可以接收對象創建,因為它只會在if(flag)路徑中使用,而不會在其他路徑中使用,并且它位于同一個嘗試中。通過此更改,在運行時只有當flag為 true時才會運行。在不涉及太多技術細節的情況下,我們可以關注的是實際的對象創建,但加載Object類仍然保留在if之前。這很難用 Kotlin 代碼來展示,因為同一條 Kotlin 行在 ART 編譯器級別會變成多條指令。

在第二個示例中,我們不能下沉代碼,因為我們將在另一個嘗試中移動實例創建(可能會拋出異常)。

Code Sinking主要是一種運行時性能優化,但它可以幫助減輕寄存器壓力。通過將指令移近其用途,在某些情況下我們可以使用更少的寄存器。使用更少的寄存器意味著更少的移動指令,這最終有助于減少代碼大小。

循環優化

循環優化有助于在編譯時消除循環。在下面的示例中, foo中的循環將a乘以10,10次。這與乘以100相同。我們啟用循環優化以在帶有 try catch 的圖中工作。

請添加圖片描述
在foo中,我們可以優化循環,因為 try catch 是不相關的。

然而,在bar或baz中,我們不對其進行優化。如果循環中有一個 try,或者整個循環是否在 try 內部,那么了解循環將采用的路徑并不是一件容易的事。

消除死代碼 – 刪除不需要的 try 塊

我們通過實施優化來刪除不包含拋出指令的 try 塊,從而改進了死代碼消除階段。我們還可以刪除一些 catch 塊,只要沒有活動的 try 塊指向它。

在下面的示例中,我們將bar內聯到foo中。之后,我們知道該師不能投擲。稍后的優化過程可以利用這一點并改進代碼。

請添加圖片描述
只需從 try catch 中刪除死代碼就足夠了,但更好的是,在某些情況下我們允許進行其他優化。如果您還記得的話,當循環有一個 try 或它位于一個 try 內部時,我們不會進行循環優化。通過消除這種冗余的 try/catch,我們可以循環優化,生成更小、更快的代碼。
請添加圖片描述

消除死代碼 – SimplifyAlwaysThrows

在死代碼消除階段,我們有一個稱為SimplifyAlwaysThrows 的優化。如果我們檢測到調用總是會拋出異常,我們可以安全地丟棄該方法調用之后的任何代碼,因為它永遠不會被執行。

我們還更新了SimplifyAlwaysThrows,以便在帶有 try catch 的圖中工作,只要調用本身不在 try 內部。如果它在 try 內部,我們可能會跳轉到 catch 塊,并且很難找出將要執行的確切路徑。

請添加圖片描述

我們還改進了:

  • 通過查看參數來檢測調用何時拋出。在左邊,我們將把divide(1, 0)標記為總是拋出,即使泛型方法并不總是拋出。
  • SimplifyAlwaysThrows適用于所有調用。以前我們有限制,例如不要對導致if的調用執行此操作,但我們可以刪除所有限制。
    請添加圖片描述

加載存儲消除 – 使用 try catch 塊

負載存儲消除(LSE) 是一種刪除冗余負載和存儲的優化過程。

我們改進了這個過程以處理圖中的 try catch。在foo中,我們可以看到,如果存儲/加載不直接與 try 交互,我們可以正常執行 LSE。在bar中,我們可以看到一個示例,我們要么走正常路徑而不拋出異常,在這種情況下我們返回1;或者我們拋出,抓住它并返回2。由于每條路徑的值都是已知的,因此我們可以刪除冗余負載。
請添加圖片描述

加載存儲消除 – 使用釋放/獲取操作

我們改進了加載存儲消除過程,以在具有釋放/獲取操作的圖表中工作。這些是易失性加載、存儲和監視操作。澄清一下,這意味著我們允許 LSE 在具有這些操作的圖中工作,但我們不會刪除所述操作。

在示例中,i和j是常規整型,vi是易失性整型。在foo中,我們可以跳過加載值,因為集合和加載之間沒有釋放/獲取操作。在bar中,易失性操作發生在它們之間,因此我們無法消除正常負載。請注意,不使用易失性加載結果并不重要——我們無法消除獲取操作。
請添加圖片描述
此優化與易失性存儲和監視器操作(Kotlin 中的同步塊)類似。

新的內聯啟發式

我們的內聯傳遞具有廣泛的啟發式。有時我們決定不內聯一個方法,因為它太大,或者有時我們決定強制內聯一個方法,因為它太小(例如,像對象初始化這樣的空方法)。

我們實現了一個新的內聯啟發式:不要內聯調用導致拋出。如果我們知道要拋出異常,我們將跳過內聯這些方法,因為拋出本身的成本足夠高,以至于內聯該代碼路徑是不值得的。

我們將跳到內聯的三個方法系列:

  • 在拋出之前計算并打印調試信息。
  • 內聯錯誤構造函數本身。
  • 最后塊在我們的優化編譯器中被重復。我們有一個用于正常情況(即嘗試不拋出),還有一個用于異常情況。我們這樣做是因為在特殊情況下我們必須:捕獲、執行finally
    塊并重新拋出。特殊情況下的方法現在不會內聯,但正常情況下的方法會內聯。
    請添加圖片描述

不斷折疊

常量折疊是一種優化過程,如果可能的話,將操作更改為常量。我們實現了一種優化,可以傳播在if防護中使用時已知為常量的變量。圖中有更多常量可以讓我們稍后執行更多優化。

在foo中,我們知道a在if保護中的值為2。我們可以傳播該信息并推斷b必須是4。同樣,在bar中,我們知道cond在if情況下必須為 true ,在else情況下必須為 false (簡化圖表)。
請添加圖片描述

把它們放在一起

如果我們考慮到本博文中的所有代碼大小優化,我們的代碼大小將減少 9.3%!

從長遠來看,一部普通手機可以有 500MB-1GB 的優化代碼(實際數字可以更高或更低,具體取決于您安裝了多少應用程序,以及您安裝了哪些特定應用程序),因此這些優化可以節省大約 50每臺設備 -100MB。由于這些優化適用于 1B+ 設備,我們在全球范圍內節省了 47-95 PB!

進一步閱讀

如果您對代碼更改本身感興趣,請隨時查看。本博文中提到的所有改進都是開源的。如果您想幫助全世界的 Android 用戶,請考慮為 Android 開源項目做出貢獻!

————————————:Santiago Aboy Solanes - 軟件工程師

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/167333.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/167333.shtml
英文地址,請注明出處:http://en.pswp.cn/news/167333.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

遞歸回溯剪枝-子集

LCR 079. 子集 - 力扣&#xff08;LeetCode&#xff09; 方法一 1. 決策樹&#xff1a;對于決策樹&#xff0c;思考的角度不同&#xff0c;畫出的決策樹也會不同&#xff0c;這道題可以從兩個角度來畫決策樹。 2. 考慮全局變量的使用&#xff1a; 使用全局變量 List<List&…

Python 基礎【五】--數據類型-序列【2023.11.24】

1.定義 Python 中的序列是一塊可存放多個值的連續內存空間&#xff0c;所有值按一定順序排列&#xff0c;每個值所在位置都有一個編號&#xff0c;稱其為索引&#xff0c;我們可以通過索引訪問其對應值。 list1 [Google, Runoob, 1997, 2000] list2 [1, 2, 3, 4, 5 ] list3…

馬克思主義基本原理課后題答案

吐血整理馬原word版課后題答案&#xff0c;大家可以自行修改&#xff0c;持續更新中... 【限于篇幅原因&#xff0c;需要的同學可以點贊收藏后&#x1f44d;&#xff0c;掃碼下方的公眾號&#xff0c;回復相應關鍵詞&#xff08;馬原&#xff09;自行領取?~】

【05】ES6:函數的擴展

一、函數參數的默認值 ES6 允許為函數的參數設置默認值&#xff0c;即直接寫在參數定義的后面。 1、基本用法 默認值的生效條件 不傳參數&#xff0c;或者明確的傳遞 undefined 作為參數&#xff0c;只有這兩種情況下&#xff0c;默認值才會生效。 注意&#xff1a;null 就…

react的開發中關于圖片的知識

React是一個流行的JavaScript庫&#xff0c;用于構建用戶界面。在React開發中&#xff0c;圖片是一個非常重要的元素&#xff0c;可以用于美化界面和展示內容。本篇博客將詳細講解React中關于圖片的知識。 1. React中使用圖片 在React中使用圖片非常簡單&#xff0c;只需要使…

【Web題】狼追兔問題

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…

解決Resolving Android Dependencies問題

無論是谷歌的Admob&#xff0c;還是Unity的Level play&#xff0c; 在windows&#xff08;win10, win11&#xff09;下&#xff0c;都出現了resolving android dependencies 報錯并且卡住的問題&#xff0c;如圖: 主要錯誤&#xff0c;是找不到這個gradlew.bat文件。 在指定位置…

什么是單元測試?

什么是單元測試 單元測試是軟件開發中的一種測試方法&#xff0c;旨在驗證各個軟件組件或模塊的功能正確性。在敏捷開發環境中&#xff0c;單元測試尤為重要&#xff0c;因為它有助于確保代碼的質量和穩定性。下面是一些關于單元測試的關鍵點&#xff1a; 定義&#xff1a;單元…

力扣每日一題-統計和小于目標的下標對數目-2023.11.24

力扣每日一題&#xff1a;統計和小于目標的下標對數目 開篇 今天這道力扣打卡題寫得我好狼狽&#xff0c;一開始思路有點問題&#xff0c;后面就是對自己的代碼到處縫縫補補&#xff0c;最后蒙混過關。只能分享一下大佬的代碼&#xff0c;然后我幫大家分享代碼的思路。 題目鏈…

大模型能否生成搜索引擎的未來?

文&#xff5c;郝 鑫 編&#xff5c;劉雨琦 ChatGPT火爆之前&#xff0c;水面下&#xff0c;也有中國公司也在朝著智能助手的方向努力。夸克便是其中之一。在GPT風靡科技圈后&#xff0c;國內就開始陸續冒出一些大模型廠商。對當時夸克而言&#xff0c;做大模型毋庸置疑&am…

django(千鋒教育)

創建一個django項目 官網下載python最新版本 配置到環境變量中 打開intlij編輯器 創建django項目 安裝django&#xff1a;pip install django 創建django項目: django-admin startproject django01 創建djangoAPP&#xff1a;python manage.py startapp App 啟動&#xff1a…

設置定時自動請求測試_自動定時循環發送http_post請求---postman工作筆記001

其實就是創建接口文件夾的時候,有個monitor collection 用來監聽接口執行情況,這里就可以設置 可以看到多久執行一次對吧,這里可以設置每幾分鐘執行一次,一共執行多少次等等 但是這里要說明一下,如果需要使用monitor功能,必須需要登錄, 所以如果這里點擊monitor collection…

媒體增加日活量的有效策略

隨著數字媒體的蓬勃發展&#xff0c;提高日活量成為媒體平臺追求的重要目標之一。日活量的增加不僅意味著更廣泛的影響力&#xff0c;還能為媒體平臺帶來更多的商業機會。以下是一些有效的策略&#xff0c;可幫助媒體提高日活量&#xff1a; admaoyan貓眼聚合 內容優質化&#…

**QT與目標板聯合調試_斷點仿真**

原文地址: https://blog.csdn.net/u012851408/article/details/86715626

仙女麻麻看過來~這是不是你們在找的外套?

分享女兒的秋冬穿搭 時尚與美觀兼具的毛毛外套 洋氣百搭不挑人穿 誰穿對都好看系列 經典寬松版型 不臃腫對身材包容性很強 小編墻裂推薦哦&#xff01;&#xff01;

NFT Insider115:The Sandbox開設元宇宙Diorama快閃店,?YGG Web3 游戲峰會已開幕

引言&#xff1a;NFT Insider由NFT收藏組織WHALE Members、BeepCrypto聯合出品&#xff0c;濃縮每周NFT新聞&#xff0c;為大家帶來關于NFT最全面、最新鮮、最有價值的訊息。每期周報將從NFT市場數據&#xff0c;藝術新聞類&#xff0c;游戲新聞類&#xff0c;虛擬世界類&#…

RevCol:可逆的柱狀神經網絡

文章目錄 摘要1、簡介2、方法2.1、Multi-LeVEl ReVERsible Unit2.2、可逆列架構2.2.1、MACRo設計2.2.2、MicRo 設計 2.3、中間監督 3、實驗部分3.1、圖像分類3.2、目標檢測3.3、語義分割3.4、與SOTA基礎模型的系統級比較3.5、更多分析實驗3.5.1、可逆列架構的性能提升3.5.2、可…

貴金屬交易指南:如何在市場中獲利?

貴金屬市場一直以來都是投資者追逐利潤的熱門選擇&#xff0c;然而&#xff0c;貴金屬市場波動較大&#xff0c;在市場中獲利并非易事。想要成功&#xff0c;需要理解市場動態和采取適當的策略。萬洲金業將為您提供一些實用的貴金屬交易指南&#xff0c;幫助您在市場中獲利。 …

PostgreSQL create or replace view和重建視圖 有什么區別?

一、 replace vs 重建 遇到開發提了個問題&#xff0c;create or replace view和重建視圖&#xff08;dropcreate&#xff09;有什么區別&#xff0c;查詢資料整理了一下。 1. create or replace 當存在同名視圖時&#xff0c;嘗試將其替換新視圖語句必須與現有視圖查詢具有相…

LeetCode算法題解(動態規劃,背包問題)|LeetCode1049. 最后一塊石頭的重量 II、LeetCode494. 目標和

一、LeetCode1049. 最后一塊石頭的重量 II 題目鏈接&#xff1a;1049. 最后一塊石頭的重量 II 題目描述&#xff1a; 有一堆石頭&#xff0c;用整數數組 stones 表示。其中 stones[i] 表示第 i 塊石頭的重量。 每一回合&#xff0c;從中選出任意兩塊石頭&#xff0c;然后將…