文章目錄
- 前言
- 人臉識別會議系統性能優化
- 一.專案背景
- 1.簡介
- 2.問題以及所遇到的挑戰
- 1.問題
- 2.挑戰
- 二.解決問題的步驟
- 1.復盤設計
- 2.資料分析
- 1.渲染模式分析
- 2.分析耗電情況
- 3.執行緒活動與CPU分析
- 4.資料匯總
- 3.定位問題
- 4.分析問題
- 5.解決問題
- 1.開啟影片原生驅動
- 2.使用互動管理器 InteractionManager
- 3.重新渲染
- 6.驗證問題(性能監控平臺的應用)
- 1.Google Android Vitals + Firebase
- 2.友盟+ U-APM
- 2.1產品概述:
- 2.2開發準備
- 2.3集成SDK
- 2.4權限授予
- 2.5混淆設定
- 2.6初始化sdk
- 三.專案總結
前言
在這個“用戶體驗為王”的時代,如何能最大程度保障產品服務的穩定性,提供良好的用戶體驗是當前開發者需要思考和亟待解決的問題,由服務開發者11年之久的全域資料服務商——友盟+發起了2021移動應用性能挑戰賽,友盟+總裁、友盟+技術負責人、CSDN副總裁、思否CTO、阿里移動端飛豬、閑魚負責人等人共同組成專家評審組,經過評選,有幸獲得了第一名,現給大家分享下作品內容,大家也可以在評論區討論心得,
人臉識別會議系統性能優化
一.專案背景
1.簡介
公司是一家物聯網公司,意向自研一款人臉會議考勤簽到面板機,為了提高產品競爭力,主打性價比+定制化差異(硬體便宜好用,軟體頁面炫酷叼炸天);軟體研發部需要支撐配套的人臉會議考勤安卓平板應用,主要業務功能有:人臉識別功能(人臉采集、對比識別、人臉庫管理),會議模塊,考勤簽到功能,定制化互動模塊,

人臉識別互動示意圖
與硬體產品經理的溝通后,提供一套樣機和一套產品清單來支撐軟體研發的開發和測驗,
主板是RK3288四核 1.8GHZ.2g記憶體,8G存盤的板子,安卓5.1的作業系統,螢屏是15.6英寸 1920*1080解析度 10點式電容觸摸屏,

RK3288主板示意圖

RK3288主板核心引數
框架選型是使用的React Native+tracker.js,考慮控制成本,沒有集成市面上的Android人臉識別SDK,通過經驗使用tracker.js替代opencv實作端的人臉識別捕捉,服務端實作人臉對比(這里為后續的錯誤埋下了伏筆),經過一段時間加班加點,開發了人臉會議考勤系統V0.1-Alpha版,
采購流程是比較慢的,等到開發板到了開始一輪真機測驗運行,搞了一輪以后組員說束手無策,別的機子都好好的,就這個不行,懷疑硬體問題,
排除硬體原因后,我整體的牽頭開始對于系統進行性能優化,
2.問題以及所遇到的挑戰
1.問題
1.人臉識別不流暢,人畫不同步,明顯延遲,人員高頻次出入鏡頭框會伴隨頓挫感,
2.POE供電,長時間運行軟體運行發熱發燙,
3.概率偶發性閃退現象,捕捉不到有價值的例外日志,
2.挑戰
1.沒有使用純安卓開發(組員基本不會安卓原生開發)+人臉識別安卓SDK(控制成本的訴求),限制了性能的上限,
2.硬體性能低,RK3288處理器,搭載的Mali-T764GPU,在14年當年算是神U,被譽為國產最強ARM處理器,但是已經6、7年過去了,我們采用的也是基礎版,安卓板人臉機還需要內置一些其他相關軟體,對性能和穩定性的要求還是比較高的,
3.概率性存在ANR/閃退崩潰問題,報錯模糊,定位不到問題,
4.組員整體為前端開發人員,對于app的優化和除錯經驗不足,
二.解決問題的步驟
1.復盤設計
忠告,先不要盯著問題本身,尤其是對于性能問題,這是大忌,這是對于很多開發人員都容易犯的錯誤,甚者用精妙的技巧去掩蓋系統設計上的缺陷,(產品設計,架構設計,原型設計,互動設計,UI設計等等)
如果系統運行或者測驗中出現了遠高于閾值的問題,第一步一定是先回過頭來看系統的看設計,(一定是)
沒有經驗的程式員會一頭扎入bug中,富有經驗的程式員會利用自有的思維方式了解問題,定位問題,分析問題,解決問題,驗證問題,而作為一個合格的架構師,或者技術團隊的leader,一定要學會“揪頭發”思維,
很多系統需要優化的問題,往往并不僅僅是一個技術問題,根源上可能是一個不合理的產品設計,冗余的架構,反人類的互動,層次過深的UI導致的,而由于系統的復雜度和團隊的溝通成本以及后期需求變動與場景的細化,往往在專案初期有些問題是很難暴露的,所以對于軟體系統的性能優化,第一步要復盤之前的設計與行為是否合理,
而事實上,所謂的差異化設計,在通過梳理精簡,剔除掉不合理因素后,對于一個工業平板app它的影片和互動還是太復雜了,
2.資料分析
本專案人臉檢測驗收標準:
包大小:~ 100M
最小人臉檢測大小:50px * 50px
可識別人臉角度:yaw ≤ ±30°, pitch ≤ ±30°
檢測速度:100ms 720p*
追蹤速度:50ms 720p*
人臉檢測耗時:< 200ms
人臉庫檢索速度:< 100ms
檢測+識別全流程耗時 < 500ms(app其他性能指標不做過多敘述)
工程化的一個要素就是用設定的標準去衡量離散型資料,如果優化沒有可量化的渲染性能評判標準,就是開發者\leader拍腦門決定了,所以不僅僅是測驗人員需要了解這些指標,開發者也要學會使用測驗工具去定位問題、驗證資料,
ok開始行動, Android adb網路連接安卓主板測驗,安裝apk,
1.渲染模式分析
打開安卓開發者模式,檢查 GPU 渲染速度和過度繪制,篩選出渲染壓力過大的頁面,

GPU 渲染模式分析示意圖

渲染顏色說明
過度繪制:實際上對于過度繪制相關的優化,要考慮投入產出比,過于精細的優化整體產出是不高的,該專案中只對于過度繪制紅色區域(過度繪制4次及以上區域)進行優化,
2.分析耗電情況
由于軟體伴有運行發熱發燙的現象,那么一定要分析耗電情況,
耗電統計是系統組件,也就是說系統運行他就一直在統計,所以獲取統計報告的時候需要將統計重置,
1.先斷開adb服務,然后開啟adb服務
殺死adb服務:執行adb kill-server 防止沖突和臟資料,
重啟adb: 執行adb devices或者adb start-server
2 .重置電池資料收集
adb shell dumpsys batterystats --enable full-wake-history
adb shell dumpsys batterystats --reset
正常情況下,我們應該斷開充電器并斷開usb連接(連接時充電),這樣會大大影響統計有效性,但是由于我們是poe供電,具體情況具體分析,使用資料輔助查找例外點,因為我們是5.1系統,所以使用adb命令:

由于txt報告實在是比較大,10幾個m肉眼看不太現實,一般都配合Battery Historian這個工具來使用,
(注意:Battery Historian是android 5.0(api 21)及以上使用,如果有幸還在使用安卓4.4工業面板的可以略過此條了,)

Battery Historian示例圖
3.執行緒活動與CPU分析
執行緒活動與CPU分析 工具有很多,但是Android Studio自帶的他不香嗎?(Rn安卓打包還是用Android Studio,使用vscode打包坑太多了,)
針對例外點進行分析,

Android Studio CPU 分析器 示例圖(https://developer.android.com/)
4.資料匯總
資料顯示CPU的負擔過重,tracking導致行程有阻塞現象,
實際上大家一直認為是完全由于渲染壓力大導致的頁面卡頓,(渲染是RN 整個框架的瓶頸),報表資料顯示的恰恰相反,對于人臉識別,GPU并沒跑滿,圖形界面的渲染作業只有部分由GPU進行的,當tracking阻塞后會暫時等待發生卡頓,再逐個完成canvas 關鍵點渲染定位,呼叫介面,取得回傳資料后渲染資訊卡片和執行影片時導致第二次輕微卡頓(RN渲染卡頓),然后性能反應正弦函式波動,同時卡頓和不流暢現象消失,
導致“拍腦袋”定位問題就是因為前端同事對于日志和資料分析工具的使用是普遍不夠的,
3.定位問題
定位問題的方法有多種,像大家常用的二分查找法(二分注釋、二分回滾),或者 斷點除錯、分析日志,都可以有效的幫助我們快速定位問題,
那么通過資料的分析以及工具提供的關鍵類,我們也是比較清晰的找出了問題:資訊卡片影片+canvas特效+人臉識別相關函式,
4.分析問題
原有的實作方式:引入全部的相關js,new多個tracking.objectTracker來檢測人臉、眼睛、嘴的區域,在通過canvas實作人臉關鍵點的展示效果,

而對人臉進行采集,Tracking.js 是使用 CPU 進行計算的,在影像的矩陣運算效率上,相對 GPU 要慢一些,
此時,有了資料的支撐,決定替換人臉識別框架層配合RN進行嘗試性優化,采用face-api.js
face-api.js
基于 TensorFlow.js 內核,實作了三種卷積神經網路架構,用于完成人臉檢測、識別和特征點檢測任務;其內部實作了一個非常輕巧,快速,準確的 68 點面部標志探測器,支持多種 tf 模型,微小模型僅為 80kb,另外,它還支持 GPU 加速,相關操作可以使用 WebGL 運行,
核心原理是針對人臉檢測作業實作了一個 SSD(Single Shot Multibox Detector)演算法,它本質上是一個基于 MobileNetV1 的卷積神經網路(CNN),在網路的頂層加入了一些人臉邊框預測層,

face-api面部標志探測器
確認替換后,針對于React Native執行緒調度做一下調優,為了方便理解,我簡單繪制了一個示意圖,講解下流程:
?JS Thread:React 等 JavaScript 代碼都在這個執行緒執行,
?Bridge:連接橋,具有異步,序列化,批處理的特點
?Shadow Thread:進行布局計算和構造 UI 界面的執行緒,
?Native modules提供 Native 功能(比如相冊、藍牙)
?UI Thread:Android/iOS(或其它平臺)應用中的主執行緒,

ReactNative執行緒示意圖
比如我們繪制一個UI,JS thread會先對其序列化,形成一條UIManager.createView 訊息,然后通過Bridge發到Shadow Thread,Shadow Tread接收到這條資訊后,先反序列化,形成Shadow tree,再轉換原生布局資訊,傳給UI thread,
而UI thread 拿到訊息后,同樣先反序列化,然后根據所給布局資訊,進行繪制,
而這一系列都強依賴于 bridge,像高度計算、UI更新每次的操作都通過 bridge傳遞,任務一多,就會生成任務佇列,異步操作批量處理,一些前端的更新很難及時反應到 UI 上,特別是類似于更新頻率較高的影片操作,任務較多,很難保證每一幀及時渲染,
那么,優化的方向:
1.減少 JS Thread 和 UI Thread 之間的異步通信,或者減少較少JSON的大小
2.盡量減少 JS Thread 側的計算
5.解決問題
整體解決方案是face-api替代tracker;React Native做一下調優,下面主要分三步講下React Native調優,
1.開啟影片原生驅動
useNativeDrive: true
JS Thread 和 UI Thread 之間是通過 JSON 字串傳遞訊息的,對于一些非布局的屬性、直接事件,(useNativeDriver 這個屬性只能使用到只有非布局相關的影片屬性上,例如 transform 和 opacity,布局相關的屬性,比如說 height 和 position 相關的屬性,開啟后會報錯,)比如人臉識別成功,人員資訊卡片影片,我們可以使用 useNativeDrive: true 開啟原生影片驅動,
Animated.timing(this.state.animatedValue, { toValue: 1, duration: 500, useNativeDriver: true, // <-- Add this }).start();
通過啟用原生驅動,我們在啟動影片前就把其所有配置資訊都發送到原生端,利用原生代碼在 UI 執行緒執行影片,而不用每一幀都在兩端間來回溝通,如此一來,影片一開始就完全脫離了 JS 執行緒,因此此時即便 JS 執行緒被卡住,也不會影響到影片了,
2.使用互動管理器 InteractionManager
使用InteractionManager將部分需要優化的任務在互動操作和影片完成之后再執行,比如:會場分布的跳轉影片,目的是平衡復雜任務和互動影片之間的執行時機,
const handle = InteractionManager.createInteractionHandle();// 執行影片... (`runAfterInteractions`中的任務現在開始排隊等候)// 在影片完成之后開始清除句柄:InteractionManager.clearInteractionHandle(handle);// 在所有句柄都清除之后,現在開始依序執行佇列中的任務
根據官方解釋的解釋:runAfterInteractions接受一個回呼函式,或是一個PromiseTask物件,該物件回傳一個Promise,如果提供的引數是一個PromiseTask, 那么即便是異步的它也會阻塞任務佇列,直到它執行完畢后,才會執行下一個任務,這樣就可以按需優化影片流暢度,
3.重新渲染
首先,RN與React中,當父組件中觸發setState, 未修改任何state中的值也會引起所有子組件的重新渲染, 或者當父組件傳給子組件的props發生改變, 不管該props是否被子組件用到, 也都會去重新渲染子組件,
那么,針對重新渲染問題,使用PureComponent和shouldComponentUpdate對于普通函式進行優化;對于hook組件使用memo優化;
至驗證后整體得到改善,互動較為流暢,達到基本性能指標,現在主要是針對于概率性問題是否復現,尋求測驗同事的幫助,
6.驗證問題(性能監控平臺的應用)
首先為什么要使用性能監控平臺:1.處理重復資訊,避免一些問題在多個APP上重復處理,或者在一個APP上反復處理;2持續捕捉重要可疑資訊,提升效率,降低人力成本,
其次什么時候、什么場景下使用性能監控平臺:除了測驗、運維需要使用性能監控平臺,開發者也要學會利用性能監控平臺去輔助定位解決問題,這里推薦兩個方案:
1.Google Android Vitals + Firebase
Android vitals是Google為提高Android設備穩定性和性能而推出的一項計劃, Google Play 的Android vitals控制臺可以突出顯示崩潰率、ANR 發生率、喚醒次數過多以及喚醒鎖定被卡住等指標,包含了開發者常用功能,關鍵是不侵入代碼,應用比較方便,
而Firebase除此之外還可以獲取詳細的自定義崩潰報告資料,以了解應用中出現的崩潰情況,該工具會按相似堆疊軌跡將崩潰分門別類,并根據崩潰對用戶所產生影響的嚴重程度進行分級,除了接收自動生成的報告外,還可以通過記錄自定義事件來獲知導致應用崩潰的操作,


Vitals + Firebase功能對比圖(圖片來源官網)
所以一般情況下使用Android Vitals可處理大部分簡單問題,并可搭配Firebase靈活處理自定義事件,
不太方便的是Google國內限制,需要公司申請專線跨境聯網,并且網路波動時,經常需要身份驗證(這點比較煩人),
費用上:Android Vitals使用免費,但是需要25$注冊開發者賬號;Firebase有免費版和付費版,適合外企、跨國公司或者有相關資質的公司研發使用,
2.友盟+ U-APM
2.1產品概述:
由于Google國內限制,很多企業沒有網路報備不能連接外網,那么友盟+ 的U-APM也可以完美滿足以上需求,針對于我的專案,我這里是選擇接入友盟+SDK協助問題檢測,
友盟的推送和統計在業界做的是比較好的,而比較熟悉友盟的朋友應該了解U-APP的穩定性功能,那么U-APM就是友盟+在U-APP穩定性功能的基礎上升級推出的一款面向開發者監控應用的穩定性資料產品,

為什么選擇友盟+ U-APM 應用性能監控平臺:
該產品不僅通過發現線上問題-快速定位問題-高效解決問題打造體系化線上質量監控平臺,而且擁有支持實時監控線上App崩潰趨勢,7*24小時監控告警與修復驗證,復現用戶崩潰現場,關鍵環節的重點監控,修復測驗等特點,
重點還在于有阿里技術的加持,可以提供長期穩定的產品迭代和專案服務及專家咨詢能力,貼心啊,企業工程化需要的就是長期穩定!小廠的產品可能用著用著就找不到人了,

2.2開發準備
如果之前有使用過U-APP的,可以直接查看官網的升級說明按體驗U-APM;那么沒有使用過友盟產品的需要到 【友盟+】官網.
注冊并且添加新應用,獲得AppKey,
注:請一定認真閱讀U-APM合規指南,滿足工信部相關合規要求,避免因隱私政策風險導致APP下架,
2.3集成SDK
maven自動集成:
maven自動集成是比較簡單快速的
首先在工程build.gradle配置腳本中buildscript和allprojects段中添加【友盟+】sdk 新maven倉庫地址,如下圖,


然后在工程App 對應build.gradle配置腳本dependencies段中添加SDK庫依賴,是不是很簡單呢,
dependencies {
implementation fileTree(include:['*.jar'], dir:'libs')
// 下面各SDK根據宿主App是否使用相關業務按需引入,
implementation 'com.umeng.umsdk:common:9.4.4'// 必選
implementation 'com.umeng.umsdk:asms:1.4.1'// 必選
implementation 'com.umeng.umsdk:apm:1.4.2' // 必選
}
手動Android Studio集成:
那么我這里是采用的手動集成
1.首先在選擇U-APM SDK組件并下載,解壓.zip檔案得到相應組件包

得到如下檔案:

umeng-common-9.4.4.jar // 統計SDK 必選
umeng-asms-armeabi-v1.4.1.aar // 必選
以及apm目錄下的
umeng-apm-armeabi-v1.4.2.aar//U-APM SDK 必選
可如有UTDID需求,集成thirdparties下
utdid4all-1.5.2.1-proguard.jar UTDID服務的補充包
如需要ABTest模塊,可集成common下
umeng-abtest-v1.0.0.aar ABTest模塊
2.在Android Studio的專案工程libs目錄中拷入以上jar包,
右鍵Android Studio的專案工程 —> 選擇Open Module Settings —> 在 Project Structure彈出框中 —> 選擇 Dependencies選項卡 —> 點擊左下“+” —> 選擇組件包型別 —> 引入相應的組件包,
3在app的build.gradle檔案中引入相應的組件包,參考示例如下:
repositories{
flatDir{
dirs 'libs'
}
}
dependencies {
implementation fileTree(include:['*.jar'], dir:'libs')
implementation (name:'umeng-asms-armeabi-v1.4.1', ext:'aar')
implementation (name:'umeng-apm-armeabi-v1.4.2', ext:'aar')
}
注意:如果需要適配armeabi 以外的平臺,或者遇到了多CPU架構so庫加載失敗問題[SA10070],除了需要引入相應的包,還要分別下載并考入對應的.so檔案,
2.4權限授予
按照官網教程授予如下權限:

<manifest ……>
<uses-sdkandroid:minSdkVersion="8"></uses-sdk>
<uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permissionandroid:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>
<uses-permissionandroid:name="android.permission.INTERNET"/>
<application ……>
2.5混淆設定
如果APP中使用了代碼混淆,需要增加如下配置
-keep class com.umeng.** { *; }
-keep class com.uc.** { *; }
-keep class com.efs.** { *; }
-keepclassmembers class *{
public<init>(org.json.JSONObject);
}
-keepclassmembers enum *{
publicstatic**[] values();
publicstatic** valueOf(java.lang.String);
}
2.6初始化sdk
在rn的安卓原生的application.onCreate函式中呼叫基礎組件包提供的初始化函式:
/**
* 注意: 即使您已經在AndroidManifest.xml中配置過appkey和channel值,也需要在App代碼中調
* 用初始化介面(如需要使用AndroidManifest.xml中配置好的appkey和channel值,
* UMConfigure.init呼叫中appkey和channel引數請置為null),
*/
UMConfigure.init(Context context,String appkey,String channel,int deviceType,String pushSecret);
或者呼叫此預初始化函式
public static void preInit(Context context,String appkey,String channel)
然后打開日志開關
**
*設定組件化的Log開關
*引數: boolean 默認為false,如需查看LOG設定為true
*/
UMConfigure.setLogEnabled(true);
至此即可使用卡頓分析功能、Java、Native崩潰分析、ANR分析功能等等基礎功能了,因為其原理通過主執行緒的回應時間,將有卡頓體驗的設備資訊、卡頓日志進行上報,那么等待設備上報后我們可以在web控制臺看到上傳的Error(列印SDK集成或運行時錯誤資訊),Warn(列印SDK警告資訊),Info(列印SDK提示資訊),Debug(列印SDK除錯資訊),以及報表,

但是從報文直接看錯誤堆疊非常麻煩, U-APM利用聚合演算法提供了卡頓模塊的功能,篩選影響用戶量大的200個堆疊從堆疊頂到堆疊底雙向聚合,展示出現頻率前10的模塊,子樹深度最多支持50層,幫助下挖詳細的卡頓模塊資訊,

除此之外,U-APM中還提供了啟動分析、記憶體分析、網路分析,用戶細查模塊等高級功能,除了記憶體分析外是其他功能需要進行配置才能使用的,大家可以去體驗一下,
那么最終通過U-APM也是順利的驗證問題、解決問題,完成了整個研發倍訓,感興趣的話,可以免費體驗U-APM.
三.專案總結
1.不要盯著問題看,對于app的性能優化也好,系統優化也好,問題的表象可能是由于本質的副作用帶來的,例如,本專案中區域現象是卡頓、不流暢,只盯著現象,我們很可能陷入優化困境,去優化渲染、減少canvas繪圖,甚至精簡業務,而最終突破我們的性能瓶頸是通過修改實作方式達成的,更適合業務場景、更能發揮機器性能,而這一切,需要資料支撐,
2. 用資料說話,不要憑感覺,去檢測性能問題、評估性能優化的效果,要有可量化的渲染性能評判標準,以及可量化、可視化的優化工具,利用經驗去感覺、猜測對于團隊是沒有沉淀的,而資料和工具是可以傳承的,例如:對于優化性能如果沒有標準,對于結果沒有資料體現,那么整體的作業是沒有意義的,成功與否全靠leader拍腦門決定,
3.使用低配置的設備:同樣的程式,在低端配置的設備中,相同的問題會暴露得更為明顯,例如:在前期安卓開發真機上并沒有卡頓現象,放在工業真機上才暴露出卡頓等問題,而對于高低端設備都能帶來很好的用戶體驗,一直是一個很重要的問題,
4.權衡利弊:在能夠保證產品穩定、按時完成需求的前提下去做優化,投入產出比過高時,應采取其他方案,切勿過度優化,永遠不要忘記,優化性能的目的是提高用戶體驗,而不是炫技,
5.拋棄沉沒成本:對于研發中已經付出且不可識訓的成本,不要影響未來的決策,例如:對于已經使用track開發的人臉識別模塊,資料證明選型影響到了性能,投入產出比在可接受范圍內,越早替換預期收益越高,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/337761.html
標籤:其他
