在業(yè)內(nèi)苫拍,Android手機一直有著“越用越慢”的口碑芜繁。根據(jù)第三方的調(diào)研數(shù)據(jù)顯示,有77%的Android手機用戶承認(rèn)自己曾遭遇過手機變慢的影響绒极。他們不明白為什么購買之初“如絲般順滑”的Android手機骏令,在使用不到一年之后都會“卡頓”得讓人抓狂!根據(jù)我們初步的測試數(shù)據(jù)垄提,手機長期所使用產(chǎn)生的磁盤碎片可以使得磁盤的寫入效率下降為原來的50%榔袋。是不是有一種“嚇?biāo)辣緦殞毩恕钡母杏X。
那么怎么辦呢铡俐?筆者曾經(jīng)對這一問題進(jìn)行分析凰兑,且讓我一一向你道來。
故事起因
故事的起因是审丘,針對“Android系統(tǒng)越用越卡的問題”吏够,騰訊某產(chǎn)品團隊希望在自身產(chǎn)品中進(jìn)行優(yōu)化,從而提升產(chǎn)品口碑滩报。
經(jīng)過簡單的分析討論稿饰,大家認(rèn)為造成這種現(xiàn)象可能是由于兩個方面原因:內(nèi)存、磁盤:
先說內(nèi)存露泊。為了保證應(yīng)用可以快速被再次調(diào)起,Android在內(nèi)存管理上采用如下策略:進(jìn)程保持在內(nèi)存中旅择,在占用內(nèi)存未超過閾值之前不會系統(tǒng)進(jìn)行主動清理惭笑。但隨著應(yīng)用的增多,試圖保持在內(nèi)存中的進(jìn)程將會增多生真,因此影響系統(tǒng)的流暢度沉噩。可以說柱蟀,內(nèi)存與系統(tǒng)卡頓的關(guān)系早已是業(yè)界的共識川蒙,其解決方案也比較明了,即賦予系統(tǒng)主動清理內(nèi)存的能力长已,例如待機后殺掉不必要的進(jìn)程畜眨。
再聊磁盤昼牛。長期使用Android手機必將產(chǎn)生大量的磁盤碎片,而磁盤碎片將會降低磁盤的讀寫性能康聂,從而影響系統(tǒng)流暢度贰健。但是磁盤碎片是否能對磁盤的讀寫性能造成了很大影響,以至于影響系統(tǒng)流暢度尚未可知,且暫時也沒有發(fā)現(xiàn)可以進(jìn)行嘗試的潛在優(yōu)化點恬汁。
于是伶椿,產(chǎn)品團隊找到了我們專項測試組,希望分析Android越用越卡與磁盤是否有關(guān)系氓侧,并初步探索系統(tǒng)在磁盤管理模式方面是否存在潛在優(yōu)化點脊另。這就有了下文。
逐步分析
Step 0:磁盤與系統(tǒng)流暢度的關(guān)系
其實约巷,日常的生活經(jīng)驗(例如SSD可以讓老筆記本煥發(fā)新生)已經(jīng)我們能夠感覺得到磁盤對系統(tǒng)流暢度的影響很大偎痛。但是,這里還是有再必要簡單說一下载庭,磁盤是如何影響系統(tǒng)流暢度的看彼。
開發(fā)過Android項目的同學(xué)都知道Android在使用網(wǎng)絡(luò)的最佳實踐是使用3級緩存的設(shè)計來提升系統(tǒng)的流暢度并節(jié)省流量:CPU首先嘗試從內(nèi)存中加載圖片,若此時圖片存在在內(nèi)存中則加載成功囚聚,否則內(nèi)存會從磁盤中加載圖片靖榕,若此時圖片存在在磁盤中則加載成功,否則磁盤會最終向網(wǎng)絡(luò)中下載圖片顽铸。
其實上述的執(zhí)行邏輯茁计,也就解釋了磁盤是如何影響系統(tǒng)流暢度的:對于系統(tǒng)流暢度(其實也是各個應(yīng)用的流暢度)影響最直接的就是CPU的執(zhí)行效率,但是如果這個過程中內(nèi)存谓松、磁盤以及網(wǎng)絡(luò)的讀寫速度如果跟不上CPU的執(zhí)行效率的話星压,就會造成CPU在處理任務(wù)的時候需要花費時間等待數(shù)據(jù),從而影響了流暢度鬼譬。
所以第一個問題就弄清楚了:磁盤的讀寫速度的降低會使得系統(tǒng)流暢度變差娜膘!那么,我們要分析的問題就轉(zhuǎn)化成:磁盤在長期使用的過程中优质,其讀寫速度會不會降低竣贪。
Step 1:弄清Android磁盤的讀寫機制
為了分析清楚磁盤“磁盤在長期使用的過程中,其讀寫速度會不會降低”這個問題巩螃,我們有必要先弄明白Android磁盤所采用的讀寫機制演怎。
通過資料查閱,我們了解到目前避乏,Android手機大多采用NAND Flash架構(gòu)的閃存卡來存儲內(nèi)容爷耀。NAND Flash的內(nèi)部存儲單位從小到大依次為:Page、Block拍皮、Plane歹叮、Die跑杭,而一個Device上可以封裝若干個Die。下圖就是一個NAND Flash組成結(jié)構(gòu)的示意圖盗胀。
為了方便理解艘蹋,針對一個Die,我們再抽象一下票灰,Page女阀、Block、Plane屑迂、Die的關(guān)系如下圖所示浸策。
雖然NAND Flash的優(yōu)點多多,但是為了延長驅(qū)動器的壽命手报,它的讀寫操作均是以Page為單位進(jìn)行的蚯舱,但擦除操作卻是按Block為單位進(jìn)行的。
由于有大量的讀寫操作掩蛤,于是我們的NAND Flash制定了如下的讀寫規(guī)則:
- 刪除數(shù)據(jù)時枉昏,芯片將標(biāo)記這些Page為閑置狀態(tài),但并不會立馬執(zhí)行擦除操作揍鸟。
- 寫入數(shù)據(jù)時兄裂,如果目前磁盤剩余空間充足,則由芯片指定Block后直接按Page為單位進(jìn)行寫入即可阳藻。
- 寫入數(shù)據(jù)時晰奖,如果目前磁盤剩余空間不足,為了獲得足夠的空間腥泥,磁盤先將某塊Block的內(nèi)容讀至緩存匾南,然后再在該Block上進(jìn)行擦除操作,最后將新內(nèi)容與原先內(nèi)容一起寫入至該Block蛔外。
那么問題來了蛆楞!假如現(xiàn)在我要向磁盤中寫入一張圖片的數(shù)據(jù),這個圖片的數(shù)據(jù)大小剛好為一個Page冒萄。最壞的情況就是,內(nèi)存中恰好只有一個Block恰好有一個Page的無效數(shù)據(jù)可以擦除橙数。為了存下這張圖片尊流,于是主控就把這個Block的所有數(shù)據(jù)讀至緩存,擦除Block上的內(nèi)容灯帮,再向緩存中加上這個4KB新數(shù)據(jù)后最后寫回Block中崖技。
我的天啊逻住,其實想存儲的就是1個Page的圖片內(nèi)容,但是實際上確造成了整個Block的內(nèi)容都被重新寫入迎献,同時原本簡單一步搞定的事情被還被分成了前后四步執(zhí)行(閃存讀取瞎访、緩存改、閃存擦除吁恍、閃存寫入)造成延遲大大增加扒秸,速度變慢。這就是傳說中的“寫入放大”(Write Amplification)問題冀瓦。而“寫入放大”也說明了磁盤在長期使用的過程中伴奥,其讀寫速度(尤其是寫入速度)會存在降低的現(xiàn)象。
Step 2:解決“寫入放大”問題的技術(shù)——TRIM
不過翼闽,既然“寫入放大”(Write Amplification)都這么出名了拾徙,肯定不會沒有現(xiàn)成的解決方案的!這個很簡單感局,Google一下尼啡,我們就知道解決方案就是TRIM技術(shù)。
TRIM是一條ATA指令询微,由操作系統(tǒng)發(fā)送給閃存主控制器崖瞭,告訴它哪些數(shù)據(jù)占的地址是“無效”的。在TRIM的幫助下拓提,閃存主控制器就可以提前知道哪些Page是“無效”的读恃,便可以在適當(dāng)?shù)臅r機做出優(yōu)化,從而改善性能代态。這里要強調(diào)下寺惫,TRIM只是條指令,讓操作系統(tǒng)告訴閃存主控制器這個Page已經(jīng)“無效”就算完了蹦疑,并沒有任何其它多余的操作西雀。在測試的過程中,我們發(fā)現(xiàn)TRIM的觸發(fā)需要操作系統(tǒng)歉摧、驅(qū)動程序以及閃存主控三者都支持才能真正意義上實現(xiàn)艇肴。例如:
- 操作系統(tǒng)不支持的情況:Android 4.3以下均不支持
- 閃存主控不支持的情況:Samsung Galaxy Nexus(I9250)所選用的閃存不支持
基于TRIM技術(shù),目前常見有兩種方案可以解決“寫入放大”的問題:
- discard選項叁温。該方案將在掛載 ext4 分區(qū)時加上 discard 選項再悼,此后操作系統(tǒng)在執(zhí)行每一個磁盤操作時同時都會執(zhí)行 TRIM 指令。該方案的優(yōu)點是總體耗時短膝但,但影響會到刪除文件時的性能冲九。
- fstrim命令。該方案將選擇合適的時機對整個分區(qū)執(zhí)行TRIM操作跟束。相對于方案一莺奸,該方案總體耗時較長丑孩,但不會影響正常操作時的磁盤性能。
不得不說灭贷,如果從用戶的角度出發(fā)温学,還是FSTRIM的方法更靠譜一些,但如何尋找合適的TRIM時機就是一個比較講究的問題了甚疟。
Step 3:TRIM在Android中的實現(xiàn)
根據(jù)前面的分析仗岖,我們不難理解在Android中的 TRIM 選擇通過fstrim命令的方式進(jìn)行實現(xiàn)。那么古拴,Google又是如何設(shè)計觸發(fā)TRIM的時機呢箩帚?
通過走讀Android源碼(AOSP 4.4.4),可以了解到Android通過系統(tǒng)服務(wù)IdleMaintenanceService來進(jìn)行系統(tǒng)狀態(tài)監(jiān)控并決定何時觸發(fā)TRIM黄痪。根據(jù)IdleMaintenanceService.java源碼紧帕,我們繪制了fstrim的觸發(fā)示意圖如下:
注釋:
- 有/無操作:距屏幕熄滅||屏保啟動已超過71分鐘
- 是/否電量充足:維護期20%,非維護期(充電狀態(tài)30%桅打,非充電狀態(tài)80%)
- 是/否維護超時:啟動維護已超過71分鐘
- 是/否已到維護期:據(jù)上次啟動維護超過1天
Step 4:分析閃存碎片及TRIM對磁盤I/O性能的影響
了解了這么多技術(shù)背景是嗜,那我們通過測試數(shù)據(jù)分析閃存碎片和TRIM對磁盤I/O性能的影響。根據(jù)測試目的挺尾,具體的測試設(shè)置如下:
- 測試目的:
評估閃存碎片和TRIM對磁盤I/O性能的影響
- 測試方案:
測試對象:LG Nexus 5 with cm-11-20140805-SNAPSHOT-M9-hammerhead
測試步驟:
- 重新刷機鹅搪,使用Bonnie++測試SD卡目錄的I/O性能;
- 模擬長期使用SD卡的過程(期間需要避免TRIM觸發(fā))遭铺,使用Bonnie++測試SD卡目錄的I/O性能丽柿;
- 主動觸發(fā)TRIM,使用Bonnie++測試SD卡目錄的I/O性能魂挂。
備注:
- 模擬長期使用SD卡的過程的方法:開發(fā)專用的測試應(yīng)用甫题,該應(yīng)用將向SD卡目錄不停寫入大小隨機的文件,當(dāng)SD卡剩余空間不足時將刪除所寫入的文件涂召,然后繼續(xù)上述操作直到應(yīng)用退出坠非。
- 避免TRIM觸發(fā)的方法:根據(jù)Android的觸發(fā)過程分析,只需設(shè)置屏幕常亮并即可避免TRIM的觸發(fā)果正。
- 測試數(shù)據(jù):
數(shù)據(jù)解讀:
- 通過反復(fù)擦寫SD卡炎码,可以發(fā)現(xiàn)SD卡的I/O效率指標(biāo)均存在一定幅度的下滑,其中反映磁盤空間分配性能及文件數(shù)據(jù)寫回性能的指標(biāo)下滑明顯秋泳;
- Sequential Output-Block可以反映分配磁盤文件空間的效率潦闲,經(jīng)反復(fù)擦寫SD卡后,該效率降低至原始值的15-20%迫皱,應(yīng)該是大量的磁盤閑置數(shù)據(jù)塊造成的影響歉闰;
- Sequential Output-Rewrite可以反映文件系統(tǒng)緩存和數(shù)據(jù)傳輸?shù)乃俣让?jīng)反復(fù)擦寫SD卡制造閑置數(shù)據(jù)塊后荞估,該效率降低至原始值的50%拌夏。
- 主動調(diào)用TRIM后慧耍,可以發(fā)現(xiàn)SD卡的I/O效率指標(biāo)均恢復(fù)至接近原始值水平(但仍未完全達(dá)到初始狀態(tài)的水平)既绩。
- 測試結(jié)論:
- 在TRIM無效的情況下概龄,長期使用SD卡,磁盤寫入速度會受到明顯影響;
- TRIM對因閑置數(shù)據(jù)塊造成的I/O性能下降有一定的恢復(fù)作用饲握;
- 大量的讀寫操作對SD卡造成了一定量的不可恢復(fù)的損耗私杜。
Step 5:FSTRIM系統(tǒng)自動觸發(fā)測試
完成了上面的工作,不由得讓我們大吃一鯨:原來TRIM對SD卡的讀寫速度的維護如此重要救欧!前面也說到衰粹,Android選擇FSTRIM方案的來實現(xiàn)TRIM,那么Android所設(shè)計的FSTRIM觸發(fā)時機有沒有什么問題呢笆怠?
根據(jù)Android系統(tǒng)的設(shè)定铝耻,F(xiàn)STRIM預(yù)期是每隔24小時觸發(fā)一次。所以蹬刷,接下來我們需要評估一下瓢捉,F(xiàn)STRIM能否依據(jù)上述設(shè)定成功被系統(tǒng)觸發(fā)。
- 測試目的:
分析FSTRIM能否被按時被系統(tǒng)觸發(fā)
- 測試方案:
測試對象:2臺Samsung Galaxy Nexus 及2臺LG Nexus 5
測試步驟:
- 刷機后办成,安裝常用應(yīng)用并啟動(均無SIM卡泡态,其中1臺設(shè)備開啟Wifi,另1臺設(shè)備關(guān)閉Wifi)迂卢;
- 進(jìn)行Log記錄某弦;
- 強制執(zhí)行一次FSTRIM;
- 滅屏等待30小時左右而克,提取Log記錄進(jìn)行分析靶壮。
- 測試數(shù)據(jù):
開啟WiFi | 關(guān)閉WiFi | |
---|---|---|
Samsung Galaxy Nexus | 啟動FSTRIM 1次 | 啟動FSTRIM 1次 |
LG Nexus 5 | 未啟動FSTRIM | 啟動FSTRIM 1次 |
數(shù)據(jù)解讀:
- FSTRIM大多數(shù)情況會被自動觸發(fā),但也存在無法觸發(fā)的情況拍摇;
- 根據(jù)FSTRIM的觸發(fā)邏輯亮钦,是否開啟WIFI對FSTRIM的影響主要是有無推送消息(影響滅屏條件)以及不同的耗電。
- 測試結(jié)論:
測試數(shù)據(jù)顯示FSTRIM大多數(shù)情況會被自動觸發(fā)充活,但也存在無法觸發(fā)的情況蜂莉。可能的原因是:FSTRIM對電量的要求略高混卵,所以一旦發(fā)生意外情況(如應(yīng)用的PUSH消息)終止了計劃FSTRIM的執(zhí)行之后映穗,很長時間之內(nèi)都無法再滿足FSTRIM的啟動條件。
所以幕随,如需提高其觸發(fā)頻率蚁滋,我們可以考慮降低觸發(fā)條件中對電量的要求。
總結(jié)&思考:
根據(jù)前面的分析,我們可以從Android源碼及測試數(shù)據(jù)對前面兩個問題做出回答:
- 磁盤碎片(更準(zhǔn)確的說法是SD卡中的閑置數(shù)據(jù)塊)會嚴(yán)重影響磁盤的讀寫性能辕录,可能會導(dǎo)致Android系統(tǒng)越用越卡睦霎,而Android系統(tǒng)的FSTRIM對此有恢復(fù)的作用;
- 經(jīng)過實驗分析走诞, FSTRIM并不一定能夠按期(每天一次)執(zhí)行副女。而導(dǎo)致這一問題的原因可能是IdleMaintenanceService對電量的要求過高(未充電狀態(tài)下大于80%)。
當(dāng)然蚣旱,我們可以通過一下手段對這一問題做出優(yōu)化嘗試:
- FSTRIM對電量的要求略高碑幅,如需提高其觸發(fā)頻率可以從降低觸發(fā)條件中對電量的要求;
- 在必要的情況下塞绿,可以發(fā)送特定的Intent事件沟涨,使系統(tǒng)強制觸發(fā)FSTRIM。
至此异吻,我們也大致解答了項目組提出的問題裹赴,這個故事也基本可以告一段落了。
回顧整個分析問題的過程诀浪,我發(fā)現(xiàn)篮昧,作為一名專項測試人員,盡管我并不需要實際編寫項目中的任何一句代碼笋妥,但這并不意味著我不需要了解Android及其Framework的代碼懊昨。實際上,只有在平時的學(xué)習(xí)和工作中了解其工作機制的基礎(chǔ)上春宣,我們才能設(shè)計出合理的測試方案酵颁,從而更好的完成工作。