关于程序员:人脸识别会议系统性能优化

11次阅读

共计 9902 个字符,预计需要花费 25 分钟才能阅读完成。

简介
姓名: 徐新宇
细分场景:物联网行业下挪动 app+ 人脸识别在工业安卓平板的利用
(注:公司我的项目信息脱敏,相干图片替换为网络图片,自己无心进犯版权,已表明相干出处)

一.我的项目背景
公司是一家物联网公司,动向自研一款人脸会议考勤签到面板机,为了进步产品竞争力,主打性价比 + 定制化差别(硬件便宜好用,软件页面炫酷叼炸天);软件研发部须要撑持配套的人脸会议考勤安卓平板利用。次要业务性能有:人脸识别性能(人脸采集、比照辨认、人脸库治理),会议模块,考勤签到性能,定制化互动模块。

人脸识别交互示意图(图片起源谷歌图片)
(原页面实现由辨认定位框 + 骨骼轮廓图 + 信息卡片 + 动画形成)

与硬件产品经理的沟通后,提供一套样机和一套产品清单来撑持软件研发的开发和测试。
主板是 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 版。
洽购流程是比较慢的,等到开发板到了开始一轮真机测试运行,搞了一轮当前组员说大刀阔斧,别的机子都好好的,就这个不行,狐疑硬件问题。
排除硬件起因后,我整体的牵头开始对于零碎进行性能优化。

一、问题以及所遇到的挑战

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 文件目录示意图

而对人脸进行采集。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 核心技术与劣势(图片起源友盟官网)

为什么抉择友盟 + U-APM 利用性能监控平台:
该产品不仅通过发现线上问题 - 疾速定位问题 - 高效解决问题打造体系化线上品质监控平台。而且领有反对实时监控线上 App 解体趋势,7*24 小时监控告警与修复验证,复现用户解体现场,关键环节的重点监控,修复测试等特点。

重点还在于有阿里技术的加持,能够提供长期稳固的产品迭代和我的项目服务及专家征询能力。贴心啊,企业工程化须要的就是长期稳固!小厂的产品可能用着用着就找不到人了。

U-APM 与竞品性能比照(图片起源友盟官网)

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 库依赖,是不是很简略呢。

  1. dependencies {
  2. implementation fileTree(include:[‘*.jar’], dir:’libs’)
  3. // 上面各 SDK 依据宿主 App 是否应用相干业务按需引入。
  4. implementation ‘com.umeng.umsdk:common:9.4.4’// 必选
  5. implementation ‘com.umeng.umsdk:asms:1.4.1’// 必选
  6. implementation ‘com.umeng.umsdk:apm:1.4.2’ // 必选
  7. }

手动 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 文件中引入相应的组件包。参考示例如下:

  1. repositories{
  2. flatDir{
  3. dirs ‘libs’
  4. }
  5. }
  6. dependencies {
  7. implementation fileTree(include:[‘*.jar’], dir:’libs’)
  8. implementation (name:’umeng-asms-armeabi-v1.4.1′, ext:’aar’)
  9. implementation (name:’umeng-apm-armeabi-v1.4.2′, ext:’aar’)
  10. }
    留神:如果须要适配 armeabi 以外的平台,或者遇到了多 CPU 架构 so 库加载失败问题 [SA10070],除了须要引入相应的包,还要别离下载并考入对应的.so 文件。
    2.4 权限授予
    依照官网教程授予如下权限:
  11. <manifest ……>
  12. <uses-sdkandroid:minSdkVersion=”8″></uses-sdk>
  13. <uses-permissionandroid:name=”android.permission.ACCESS_NETWORK_STATE”/>
  14. <uses-permissionandroid:name=”android.permission.ACCESS_WIFI_STATE”/>
  15. <uses-permissionandroid:name=”android.permission.READ_PHONE_STATE”/>
  16. <uses-permissionandroid:name=”android.permission.INTERNET”/>
  17. <application ……>
    2.5 混同设置
    如果 APP 中应用了代码混同,须要减少如下配置
  18. -keep class com.umeng.* {; }
  19. -keep class com.uc.* {; }
  20. -keep class com.efs.* {; }
  21. -keepclassmembers class *{
  22. public<init>(org.json.JSONObject);
  23. }
  24. -keepclassmembers enum *{
  25. publicstatic**[] values();
  26. publicstatic** valueOf(java.lang.String);
  27. }

2.6 初始化 sdk
在 rn 的安卓原生的 application.onCreate 函数中调用根底组件包提供的初始化函数:

  1. /**
    • 留神: 即便您曾经在 AndroidManifest.xml 中配置过 appkey 和 channel 值,也须要在 App 代码中调
    • 用初始化接口(如须要应用 AndroidManifest.xml 中配置好的 appkey 和 channel 值,
    • UMConfigure.init 调用中 appkey 和 channel 参数请置为 null)。
  2. */
  3. UMConfigure.init(Context context,String appkey,String channel,int deviceType,String pushSecret);

或者调用此预初始化函数

  1. public static void preInit(Context context,String appkey,String channel)
    而后关上日志开关
  2. /**
  3. * 设置组件化的 Log 开关
  4. * 参数: boolean 默认为 false,如需查看 LOG 设置为 true
  5. */
  6. UMConfigure.setLogEnabled(true);
    至此即可应用卡顿剖析性能、Java、Native 解体剖析、ANR 剖析性能等等根底性能了。因为其原理通过主线程的响应工夫,将有卡顿体验的设施信息、卡顿日志进行上报。那么期待设施上报后咱们能够在 web 控制台看到上传的 Error(打印 SDK 集成或运行时错误信息),Warn(打印 SDK 正告信息),Info(打印 SDK 提示信息),Debug(打印 SDK 调试信息)。以及报表。

    U-APM 解体信息日志示例图

然而从报文间接看谬误堆栈十分麻烦,U-APM 利用聚合算法提供了卡顿模块的性能,筛选影响用户量大的 200 个堆栈从栈顶到栈底双向聚合,展现呈现频率前 10 的模块,子树深度最多反对 50 层,帮忙下挖具体的卡顿模块信息。

U-APM 卡顿模块示例图

除此之外,U-APM 中还提供了启动剖析、内存剖析、网络分析,用户细查模块等高级性能。除了内存剖析外是其余性能须要进行配置能力应用的。大家能够去体验一下。
那么最终通过 U -APM 也是顺利的验证问题、解决问题。实现了整个研发闭环。感兴趣的话,能够收费体验 U -APM。

三.我的项目总结

1. 不要盯着问题看。对于 app 的性能优化也好,系统优化也好。问题的表象可能是因为实质的副作用带来的。例如,本我的项目中部分景象是卡顿、不晦涩,只盯着景象,咱们很可能陷入优化窘境,去优化渲染、缩小 canvas 绘图,甚至精简业务。而最终冲破咱们的性能瓶颈是通过批改实现形式达成的,更适宜业务场景、更能施展机器性能。而这所有,须要数据去撑持。

  1. 用数据谈话。不要凭感觉,去检测性能问题、评估性能优化的成果,要有可量化的渲染性能评判规范,以及可量化、可视化的优化工具。利用教训去感觉、猜想对于团队是没有积淀的,而数据和工具是能够传承的。例如:对于优化性能如果没有规范,对于后果没有数据体现。那么整体的工作是没有意义的,胜利与否全靠 leader 拍脑门决定。
    3. 应用低配置的设施:同样的程序,在低端配置的设施中,雷同的问题会裸露得更为显著。例如: 在后期安卓开发真机上并没有卡顿景象,放在工业真机上才暴露出卡顿等问题。而对于高低端设施都能带来很好的用户体验,始终是一个很重要的问题。
    4. 权衡利弊:在可能保障产品稳固、按时实现需要的前提上来做优化,投入产出比过高时,应采取其余计划,切勿适度优化。永远不要遗记,优化性能的目标是进步用户体验,而不是炫技。
    5. 摈弃沉没老本:对于研发中曾经付出且不可发出的老本,不要影响将来的决策,例如:对于曾经应用 track 开发的人脸识别模块,数据证实选型影响到了性能。投入产出比在可承受范畴内,越早替换预期收益越高。
正文完
 0