背景
寫這篇文章,主要是為了以後面試方便。因為我簡歷上寫了,上一份工作的最大亮點是將人臉解鎖的速度由1200ms優化到了600ms,所以這些內容已經回答無數遍了。但每次總覺得回答的不完整,或者說總感覺可以發揮得更好,於是這裡做一些簡單的總結性的記錄。
我2018年4月份進入到某手機公司,在其中工作了兩年多的時間,這期間主要負責人臉解鎖的功能。人臉解鎖的速度優化,是入職開始的一個很重要的任務,前前後後持續了很長時間。做優化前,首先是明確目標,我接手時人臉解鎖的速度是1200ms左右,而我們是參照的精品機Oppo、vivo、小米、華為等主流機型解鎖速度大約在400~700ms不等,所以這也就成了我優化的目標。
整個人臉解鎖的架構圖如下所示:
優化的基本手段是將整個啟動過程拆分為多個階段,然後針對每一個階段進行優化,優化流程邏輯或者採用更高效技術,歸納起來主要有如下這些步驟:
1、從按power鍵到SystemUI中的KeyguardService
優化前,使用的是廣播方式,但我們知道使用廣播方式來實現IPC效能是非常差的,速度很慢。優化後,採用AIDL取代廣播方式,提升40ms左右。
有些面試官會問:為什麼使用AIDL比使用廣播速度更快?
網上沒有搜到比較權威的答案,我自己整理了一下,我個人認為有如下幾個原因:(1)廣播的傳送和接收過程中,有多次Binder實現的IPC。廣播實現跨程式通訊的方式也通過Binder實現的,這一點和AIDL一樣。但是廣播在註冊時會將IntentFilter和Receiver資訊通過Binder方式註冊到AMS中,這裡面會有很多封裝、過濾等操作,將action,Receiver,Context、IntentFilter進行關聯。傳送者傳送訊息時會將action等資訊封裝到Intent中,然後通過Binder方式與AMS通訊,將Intent等資訊傳入AMS中。AMS中經過匹配action,找到對應的註冊者和接收者,然後再通過Binder方式和Receiver通訊,這樣就完成了一次廣播的傳送和接收,其中發生了多次Binder。而AIDL方式就過程就簡單很多,直接在傳送者和接收者之間一次Binder即可。廣播是四大元件之一,在使用過程中會存在很多中間的處理過程,比如對Intent等的中間處理等。(2)廣播有前臺廣播和後臺廣播之分,預設是後臺廣播。系統的內部存在無數的廣播,由於系統資源有限,會優先處理一些非常重要的廣播,這就使得預設情況下的廣播會低優先順序處理這些後臺廣播。同樣也由於系統資源有限,所以不能隨意將廣播設定為前臺廣播。(3)根據註冊的方式不同,廣播有靜態註冊和動態註冊的區分,如果是靜態註冊,就是並行廣播,如果有多個地方註冊了該廣播,會根據註冊的時間來依次處理廣播事件。這一點不同於序列廣播,動態註冊是序列廣播。
2、SystemUI與FaceId Service長期保持連線。
SystemUI與FaceId是通過AIDL來通訊的, 優化前,SytemUI每次和FaceId通訊完畢後都會斷開連線,這樣就導致在下次使用人臉解鎖時,必須重新建立連線,會有一定程度的延遲。優化的方法就是讓SystemUI和FaceId保持長久的連線。當然這一點縮短的時間並不太多。
3、使用Camera2 API代替Camera1 API
系統提供了Camera API-2,是官方對API-1的優化,效能更穩定,使用起來也更加方便。官方並沒有明確說明使用API-2會比API-1更加快速,但實際開發中發現,使用API-2替換後,整個開啟預覽和開啟相機的過程縮短了150ms左右。
4、提高相機幀率,儘量減少人臉解鎖演算法的等待時間
人臉解鎖的核心流程是:SDK會預先錄入使用者的人臉資料,在需要解鎖時,相機通過攝像頭以一定的幀率獲取影像資訊,通過API中的回撥將影像資訊傳遞給SDK。SDK中封裝了人臉匹配演算法,該演算法會將相機傳遞的影像資訊和預存的人臉資料進行匹配,並根據匹配結果返回對應的值,比如環境太黑、檢測不到人臉、和預存的不是同一個人、匹配成功等各種匹配結果,都有一個數字與之對應。
SDK在匹配時,如果成功,一次匹配的時間大約是30ms,如果是失敗的匹配,一次匹配大約50ms~100ms不等(在匹配過程中,如果有新的影像資料傳遞過來,會被過濾掉)。而平常使用時常常不能一次匹配成功,本次匹配失敗後,很快從相機拿下一筆影像來匹配,直到在指定時間(設定的是5s)內匹配成功。為了能夠讓本次匹配失敗後很快拿到下一筆影像,這就要求相機提高獲取影像的幀率。這一點督促相機團隊的同事,修改相機引數,綜合考慮之下,取了15ms每幀的頻率。
5、合理設定相機的初始曝光值
相機一般會預設設定一個曝光值,在不同環境中使用時,再根據周圍環境來調整,以適應周圍的光照環境。如果預設的曝光值設定不合適,會導致剛開啟相機時,得到的前幾筆影像要麼太暗,要麼太亮,需要自我調整達到一個高質量的狀態。
在優化前,由於對預設的曝光值設定不合理,導致開啟人臉解鎖功能時,即便是很正常的光照環境下,面對正確的人臉時,前面多次匹配都因為影像質量太差,導致匹配失敗。每次匹配失敗都會浪費50~100ms的時間,這就導致每次解鎖成功前,都會浪費300ms甚至更多的時間在相機自身調整曝光值上。
在後面做優化時,通過大量的測試和分析log,發現了每次匹配都不能一次成功的問題,然後將相機提供的資料轉為圖片,才發現影像的質量問題。後來和相機團隊的同事,共同除錯,得到一個比較合適的初始曝光值,解決了這個問題。
6、在啟動人臉解鎖時啟動CPU拉頻,併合理處理速度和省電之間的關係
人臉解鎖演算法執行是一個高密集計算的操作,為了提高解鎖的速度,優化過程中採用了排程CPU最大核,並提高CPU頻率的做法,使得匹配的速度有所提高。
排程CPU最大核並提高CPU頻率,是一個非常耗電的過程。為了更好地平衡匹配速度和省電,這裡又做了一個設計:人臉解鎖超時時間設定的是5s,但實際上,如果周圍環境正常,且是正確的人臉,大部分場景下都能在前1s內解鎖完成,只有在環境異常或者非正確人臉的時候,才會在1s後還需要匹配,此種場景能解鎖的概率就比較小了。所以這裡的處理方法是,在人臉解鎖的前1s排程CPU最大核,如果還沒有解鎖,則將CPU調回正常,而不是一直都使用CPU大核和高頻。
7、合理使用並行代替序列
人臉匹配成功後,就可以立刻走解鎖流程,並呼叫相機的關閉方法,而關閉相機需要150ms的時間。優化前原開發者採用的是序列的方式,也就是在人臉匹配完成後,在同一個執行緒中呼叫了關閉相機操作,相機關閉後才走鎖屏介面消失的流程。這個優化點應該是很明顯的,關閉相機的操作放在單獨的一個執行緒去執行就可以了,這樣一來就能夠再優化150ms的時間。至於原開發者為什麼要採用序列,不得而知。
當然,優化過程還有其他很多的細節,比如一些流程的時間複雜度優化,非必要流程的精簡,要求sdk人臉匹配演算法做優化,新手機中使用了效能更好的CPU、相機硬體等。