背景介绍1.1 问题描述问题一:手机系统更新为最新的api12整体推理速度变慢,在人脸识别界面点击关闭按钮,要等3秒后才会响应点击事件,ui线程卡顿。问题二:手机系统升级到26之后,ui变得卡顿,影响场景在应用人脸识别界面有个20s倒计时,正常的情况倒计时数字每秒要变化一次,卡顿导致倒计时数字会出现隔几秒才变化,严重影响正常业务。1.2 初步分析结论问题一:receiver接收相机预览流的帧数据,从ArkTS传到native,在native里接收receiver,然后获取arraybuffer做计算,就导致性能变差,c++侧算法耗时18版本是20ms,25版本大概400ms,相差20倍,系统变更导致纯c++计算性能裂化。问题二:receiver.on('imageArrival')接收相机预览流的帧数据,从ArkTS侧获取arraybuffer,传到native侧做人脸识别算法,对比分析运行trace的调用栈,发现人脸识别算法中的nativeTrack函数耗时差异明显。<p id="p17941112631715">手机版本</p> <p id="p194192631715">nativeTrack耗时</p> <p id="p159415260176">总耗时</p> <p id="p5941182641715">自身函数耗时</p> <p id="p19941192615171">libpaddle_light_api_shared.so</p> <p id="p79411826161710">3.0.018</p> <p id="p109421263171">33ms</p> <p id="p5942192681720">22ms</p> <p id="p794222651711">总耗时10ms 调用8次 最大耗时3.5ms</p> <p id="p1094211266179">3.0.0.31</p> <p id="p16942112616173">67ms</p> <p id="p109421826151718">50ms</p> <p id="p17942226161710">总耗时17ms 调用13次 最大耗时4.5ms</p> 定位定界方法2.1 排查问题step1 分析trace,对比差异对比分析trace,调用栈发现耗时差异在so,需要看so的具体实现,找出可疑的部分。step2 联合定位日志打点,缩小范围,定位一个纯c++的函数在两个版本差异明显,锁定代码行数30+。void CBankCardProcessYolo::decodeYUV420SP(MImage &imgColor, unsigned char* yuv420sp, int width, int height) { //OH_LOG_INFO(LOG_APP, "RecognizeNV21 decodeYUV420SP start"); int frameSize = width * height; int i = 0, y = 0; int uvp = 0, u = 0, v = 0; int y1192 = 0, r = 0, g = 0, b = 0; for (int j = 0, yp = 0; j < height; j++) { uvp = frameSize + (j >> 1) * width; unsigned char *src = imgColor.m_lpLine[j]; u = 0; v = 0; for (i = 0; i < width; i++, yp++) { if (y < 0) y = 0; y = (0xff & ((int)yuv420sp[yp])) - 16; if (y < 0) y = 0; if ((i & 1) == 0) { v = (0xff & yuv420sp[uvp++]) - 128; u = (0xff & yuv420sp[uvp++]) - 128; } y1192 = 1192 * y; r = (y1192 + 1634 * v); g = (y1192 - 833 * v - 400 * u); b = (y1192 + 2066 * u); if (r < 0) r = 0; else if (r > 262143) r = 262143; if (g < 0) g = 0; else if (g > 262143) g = 262143; if (b < 0) b = 0; else if (b > 262143) b = 262143; src[i * 3 + 2] = (r >> 10); src[i * 3 + 1] = (g >> 10); src[i * 3] = (b >> 10); } } }step3 内部写demo复现按照业务流程,写demo,加上可疑代码,本地稳定复现,采用最笨的方法,给每行代码前后加日志打点,确认是yuv420sp[yp]这句代码在25版本耗时长;规避方案将yuv420sp复制一份,然后用复制后的char*参与计算;长期方案提单跟踪。step1 分析trace,对比差异性能问题第一考虑抓frame trace分析,分别抓取不同手机版本的运行trace,重点看主进程泳道和ArkTS Callstack泳道。以人脸识别SDK为例,通过ArkTS Callstack对比分析发现,有大量的nativeTrack方法在主线程执行且是阻塞的,导致vsync信号没有机会上屏,也就无法刷新ui,31版本最长看到间隔7秒才有一个vsync信号,对比分析18版本在1s内至少有一次vsync信号,连续的耗时任务导致cpu没有空闲处理ui刷新。都是连续的耗时任务,继续对比分析调用栈,发现nativeTrack函数在不同的版本耗时有差异,31版本耗时更大,可能就是这个函数耗时大导致ui线程阻塞,这个函数内部有多个其它函数会调用libpaddle\_light\_api\_shared.so,汇总信息nativeTrack函数耗时主要有两部分内部自身耗时+libpaddle\_light\_api\_shared.so耗时,其中libpaddle\_light\_api\_shared.so耗时差异不大,耗时大头在内部自身代码。看代码是纯c/c++编写,没有使用到系统接口,看不出来是哪行代码导致性能裂化,想着在可疑的地方加上日志打点逐句排查,这条路试过没走通;step2 联合定位重点看nativeTrack函数,影响代码行数1000+,逐行加打点这条路走不通,没办法找出劣化的具体点。step3 内部写demo复现代码1000+行,没办法写demo验证。2.2 问题根因问题一:手机系统18升级到25后ArrayBuffer取值性能变差,经开发确认是没有使能cache,surfaceBuffer数据是由相机创建填入的,buffer创建时需要填入usage信息,这个会影响buffer获取效率。问题二:未找到根因,可以明确的是系统版本升级确实导致一段c++代码性能劣化,1s内onImageArrival会回调28次,平均30ms左右会触发一次,如果每次回调函数处理耗时50ms以上就会出现倒计时卡顿,代码太多没有办法找到劣化点。2.3 解决方案提供规避方案适配,计算完一帧数据才去处理下一帧。
1.1 问题描述
1.2 初步分析结论
2.1 排查问题
step1 分析trace,对比差异
对比分析trace,调用栈发现耗时差异在so,需要看so的具体实现,找出可疑的部分。
step2 联合定位
日志打点,缩小范围,定位一个纯c++的函数在两个版本差异明显,锁定代码行数30+。
step3 内部写demo复现
按照业务流程,写demo,加上可疑代码,本地稳定复现,采用最笨的方法,给每行代码前后加日志打点,确认是yuv420sp[yp]这句代码在25版本耗时长;规避方案将yuv420sp复制一份,然后用复制后的char*参与计算;长期方案提单跟踪。
step1 分析trace,对比差异
都是连续的耗时任务,继续对比分析调用栈,发现nativeTrack函数在不同的版本耗时有差异,31版本耗时更大,可能就是这个函数耗时大导致ui线程阻塞,这个函数内部有多个其它函数会调用libpaddle\_light\_api\_shared.so,汇总信息nativeTrack函数耗时主要有两部分内部自身耗时+libpaddle\_light\_api\_shared.so耗时,其中libpaddle\_light\_api\_shared.so耗时差异不大,耗时大头在内部自身代码。
step2 联合定位
重点看nativeTrack函数,影响代码行数1000+,逐行加打点这条路走不通,没办法找出劣化的具体点。
step3 内部写demo复现
代码1000+行,没办法写demo验证。
2.2 问题根因
问题一:手机系统18升级到25后ArrayBuffer取值性能变差,经开发确认是没有使能cache,surfaceBuffer数据是由相机创建填入的,buffer创建时需要填入usage信息,这个会影响buffer获取效率。
问题二:未找到根因,可以明确的是系统版本升级确实导致一段c++代码性能劣化,1s内onImageArrival会回调28次,平均30ms左右会触发一次,如果每次回调函数处理耗时50ms以上就会出现倒计时卡顿,代码太多没有办法找到劣化点。
2.3 解决方案
提供规避方案适配,计算完一帧数据才去处理下一帧。