背景
寫這篇文章,主要是為了以后面試方便。因為我簡歷上寫了,上一份工作的最大亮點是將人臉解鎖的速度由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、相機硬件等。