鸿蒙(HarmonyOS)性能优化实战-Trace使用教程
概述OpenHarmony的DFX子体系提供了为应用框架以及体系底座核心模块的性能办理能力,每一处办理即是一个Trace,其上附带了记载执行时间、运行时格式化数据、历程或线程信息等。开辟者可以使用SmartPerf-Host调试工具对Trace进行解析,在其绘制的泳道图中,对应用运行过程中的性能热点进行分析,得出优化方案。本文旨在先容OpenHarmony中常用的Trace,解释它们的寄义和用途,并阐述如何通过这些Trace来辨认潜在的性能问题。同时,我们还将详细先容Trace的工作原理,帮助读者更好地理解这些Trace及如何实现性能数据的收罗和分析。通过本文的阅读,读者将对OpenHarmony中的Trace有一个深入的了解,为应用步伐性能优化提供有力支持。
常用Trace及寄义
下面将从渲染流程入手,配合常用场景先容常用Trace。
渲染流程
与其他操作体系雷同,OpenHarmony也是由Vsync信号控制每一帧绘制操作的机遇。Vsync信号是一个垂直同步信号,它指示显示器在垂直空缺期之后开始下一帧的刷新。装备的屏幕以固定的频率发送Vsync信号,以刷新率60Hz举例,则屏幕每隔16.6ms发送一次Vsync信号。在收到Vsync信号后,UI后端引擎开始准备屏幕的下一帧绘制,然后应用步伐提交渲染命令,用于描述图形绘制、纹理设置、着色器使用等。一旦应用步伐提交了渲染命令,UI后端引擎会将其添加到渲染队列中,并在合适的机遇执行这些渲染命令,通常会在后台线程执行,以确保主线程不被长时间阻塞。当这些渲染命令被UI后端引擎执行时,它们会被传递给图形体系Render Service进行处置惩罚,图形体系会根据命令进行相应的图形计算和渲染操作,如顶点变更、光照、纹理贴图等。在图形体系完成渲染后,渲染结果将被写入帧缓冲区。帧缓冲区是一个内存区域,存储用于显示器输出的图像数据。一旦帧缓冲区更新完成,UI后端引擎会等候直到下一个Vsync信号到来,这个过程是为了确保渲染结果在显示器垂直消隐之前准备好。当下一个Vsync信号到来时,UI后端引擎将已经准备好的帧缓冲区的内容发送给显示器,显示器根据这些数据刷新本身的像素,至此完成一整个渲染周期。如图1所示。
图1 渲染流程图
https://i-blog.csdnimg.cn/blog_migrate/f510d819a727e070dc91ffeb7cb577d8.png
从Trace角度来看,一帧的渲染流程如下:
(1)Vsync信号到达;
(2)UI后端引擎进行第一帧绘制;
(3)向Render Service通讯,传输绘制命令并请求一帧;
(4)Render Service对多个图层进行归并,计算刷新区域,然后进行渲染和绘制本帧;
(5)完成一帧绘制后交给屏幕。
一帧的渲染流程中的UI后端引擎的常用Trace的寄义如图2所示。
图2 UI后端引擎渲染Trace泳道图
https://i-blog.csdnimg.cn/blog_migrate/d77386a69a4e66721c5be14bc42b5453.png
序号Trace参数阐明描述1OnVsyncEvent now:%" PRIu64 "当前时间戳–纳秒级收到Vsync信号,渲染流程开始2FlushVsync刷新视图同步变乱,包括记载帧信息、刷新任务、绘制渲染上下文、处置惩罚用户输入3UITaskScheduler::FlushTask刷新UI界面,包括布局、渲染和动画等4FlushMessages发送消息通知图形侧进行渲染5FlushLayoutTask执行布局任务6FlushRenderTask %zu当前页面上的需要渲染的节点的数量总渲染任务执行7Layout节点布局8FrameNode::RenderTask单个渲染任务执行9ListLayoutAlgorithm::MeasureListItem:%d当前列表项索引计算列表项的布局尺寸 图形图像子体系中的Render Service,是负责界面内容绘制的部件,处置惩罚由各个应用提交的统一渲染任务,将差别应用渲染的图层进行归并、送显。在收到每个Vsync周期信号时,起首处置惩罚应用提交的指令,包括应用渲染树节点的新增、删除、修改,然后进行动画计算和遮挡计算,以上是为了对统一渲染树进行更新。接下来开始对渲染树执行绘制,起首预处置惩罚每个节点,计算绝对位置和脏区信息,然后针对脏区进行绘制,优先使用硬件合成器进行绘制,当碰到无法合成绘制的,交由GPU执行重绘,绘制的所有结果都将存入屏幕缓冲区,最后将绘制结果提交送显、上屏展示。
当Vsync信号刷新时,如图3所示。
图3 RS侧渲染Trace泳道图
https://i-blog.csdnimg.cn/blog_migrate/1fa3c98bb8108fc9bbe8fd4855a5cf40.png
序号Trace描述1RSMainThread::DoComposition合成渲染树上各节点图层2RSMainThread::ProcessCommand处置惩罚client端指令3Animate动画处置惩罚4RSMainThread::CalcOcclusion遮挡计算5ProcessDisplayRenderNode单个显示器画面的绘制流程6ProcessSurfaceNode:x单个节点的合成器处置惩罚7Repaint硬件合成器合成绘制8Redraw无法进行合成,则执行重绘9RenderFrameGPU执行绘制10SwapBuffers刷新屏幕缓冲区11Commit绘制结果提交上屏 懒加载
懒加载使用LazyForEach实现,LazyForEach从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当LazyForEach在滚动容器中使用时,框架会根据滚动容器可视区域按需创建组件。当组件滑出可视区域外时,框架会进行组件销毁以降低内存占用。图4抓取的是懒加载过程中一帧的Trace。
图4 懒加载Trace泳道图
https://i-blog.csdnimg.cn/blog_migrate/5d8aad679d10df14afae19faf6eb71f0.png
序号Trace参数阐明描述1OnIdle, targettime:%" PRId64 "时间戳,在这个时间之前完成该任务idle变乱循环中检查是否有新的变乱需要处置惩罚,如果有,则将任务调度器加入UI线程中并执行预测任务2expiringItem_ count:[%zu]懒加载Item的个数预构建,包罗处置惩罚所有懒加载项3List predict添加预测布局任务4Builder:BuildLazyItem [%d]需创建的项目索引在需要时创建项,并进行缓存5Layout[%s]tag标签,当前节点在UINode树中的索引,父节点在UINode树中的索引当前帧节点布局6Build[%s]tag标签,当前节点在UINode树中的索引,父节点在UINode树中的索引当前帧节点构建7CustomNode:BuildRecycle %sJS视图名称触发复用渲染8ExecuteJS执行JS代码 页面加载
当触发页面加载时,OpenHarmony会创建一个新的页面实例,然后按照特定的步伐调用页面的生命周期方法。在生命周期方法中加载页面的布局,然后将数据绑定到页面上的视图元素,使页面可以或许显示和更新数据。图5抓取的是页面加载中一帧的Trace。
图5 页面加载帧Trace泳道图
https://i-blog.csdnimg.cn/blog_migrate/eefa54bcfad1bee9b392adfc68cde8e1.png
序号Trace参数阐明描述1PageRouterManager::RunPage页面路由预处置惩罚及加载页面2PageRouterManager::LoadPage加载页面并路由3JsiDeclarativeEngine::LoadPageSource加载一个JavaScript文件并将其解析为ABC字节码4JsiDeclarativeEngine::LoadJsWithModule Execute Page code : %s页面url地址执行页面代码5Build[%s]tag标签,当前节点在UINode树中的索引,父节点在UINode树中的索引当前帧节点构建6CustomNode:BuildItem %sJS视图名称渲染子节点然后将其挂载到父节点上7ViewChangeCallback(%d, %d)视图宽,视图高视图变革回调 Trace实践
以下示例采取LazyForEach的方式遍历列表,并借助SmartPerf-Host调试工具追踪代码执行流程。
在代码示例中,使用一个List容器组件,通过懒加载方式来创建出120个IconView自定义组件。在IconView组件中,使用了Flex容器组件包罗Image和Text子组件,形成了图文混合列表。
// src/main/ets/pages/LazyForEachPage.ets
@Entry
@Component
struct LazyForEachPage {
private iconItemSourceList = new ListData();
aboutToAppear() {
// 添加120个IconItem的数据
......
}
build() {
Column() {
Text('懒加载示例')
.fontSize(24)
.fontColor(Color.Black)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Start)
.width('90%')
.height(50)
List({ space: 20 }) {
LazyForEach(this.iconItemSourceList, (item: IconItemModel) => {
ListItem() {
IconItem({ image: item.image, text: item.text })
}
}, (item: IconItemModel, index) => index.toString())
}
.divider({ strokeWidth: 2, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
.width('100%')
.height('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center)
}
}
// src/main/ets/view/IconView.ets
@Component
export struct IconItem {
image: string | Resource = '';
text: string | Resource = '';
build() {
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Center, alignContent: FlexAlign.Center }) {
Image(this.image)
.height(40)
.width(40)
.objectFit(ImageFit.Contain)
.margin({
left: 15
})
Text(this.text)
.fontSize(20)
.fontColor(Color.Black)
.width(100)
.height(50)
.textAlign(TextAlign.Center)
}
.width('100%')
.height(50)
}
}
下面使用SmartPerf-Host调试工具抓取htrace文件,并生成一个跟踪泳道分析图,来了解示例代码的加载流程。跟踪泳道分析图被分为五个部门,每个部门都标注数字并框选出相应的标签,从而使得整体的过程可以或许得到更好的理解。
图6 LazyForEach遍历的列表的泳道分析图
https://i-blog.csdnimg.cn/blog_migrate/084c22e2dc683154229769374b7e681b.png
接下来,逐一解析这五个模块的详情:
1.加载并路由LazyForEach页面
图7 加载并路由LazyForEach页面泳道图
https://i-blog.csdnimg.cn/blog_migrate/7ed464455089282613461f8040f6718f.png
[*]H:JsiDeclarativeEngine::LoadPageSource加载一个 JavaScript 文件,而且解析为 ABC 字节码;
[*]H:FlushPipelineWithoutAnimation 清理渲染管道的操作;
[*]H:CustomNode:OnAppear 用于构建当前 OnAppear 生命周期的操作,并执行aboutToAppear生命周期函数;
[*]H:CustomNode:BuildItem LazyForEachPage 渲染子节点并挂载在 LazyForEachPage 页面上。
2.对当前帧节点Stage,执行布局任务、执行渲染任务并通知图形侧进行渲染
图8 对当前帧节点Stage,执行布局任务、执行渲染任务并通知图形侧进行渲染泳道图
https://i-blog.csdnimg.cn/blog_migrate/2e610217f2ccb10fad1783b346373f98.png
[*]H:Layout对当前帧节点Stage,执行布局任务;(Stage作为框架,承载着页面Page节点。因此,标签的呈现会从Stage开始)
[*]H:Measure[%s] 对Page、Column、Row、Image、Text等组件布局尺寸计算;
[*]H:Builder:BuildLazyItem 和H:ListLayoutAlgorithm::MeasureListItem:0 分别为创建一个LazyItem项目和计算列表项的布局尺寸;
[*]H:Layout[%s] 对Page、Column、Row、Image、Text等组件执行布局任务;
[*]H:FrameNode::RenderTask 执行渲染任务;
[*]H:RequestNextVSync 请求下一帧Vsync信号。
3.对当前帧节点Flex,执行布局任务、执行渲染任务并通知图形侧进行渲染
图9 对当前帧节点Flex,执行布局任务、执行渲染任务并通知图形侧进行渲染泳道图
https://i-blog.csdnimg.cn/blog_migrate/44eff0f75dc9019b56cef99b01f5bc96.png
[*]H:Layout对当前帧节点Flex,执行布局任务**;**
[*]H:Measure[%s] 对Image、Text等组件布局尺寸计算;
[*]H:FrameNode::RenderTask Flex渲染任务执行;
[*]H:RequestNextVSync 请求下一帧Vsync信号。
4.构建前预处置惩罚数据及添加预测布局任务
图10 构建前预处置惩罚数据及添加预测布局任务泳道图
https://i-blog.csdnimg.cn/blog_migrate/ce451d583dfafedb288725f22921453d.png
[*]H:Builder:BuildLazyItem 构建前预处置惩罚数据了11条数据;
[*]H:Layout 添加一条Flex、Image、Text的预测布局;
[*]H:FlushMessages 发送消息通知图形侧进行渲染。
5.合成渲染树上各节点图层任务
图11 合成渲染树上各节点图层任务泳道图
https://i-blog.csdnimg.cn/blog_migrate/ad07b6be445bdb5105c2305086b5b0dd.png
[*]H:AcquireBuffer、H:ProcessSurfaceNode:EntryView XYWH获取屏幕缓冲区并绘制EntryView、SystemUi_StatusBar、SystemUi_NavigationBar等;
[*]H:Repaint 硬件合成器合成绘制当前节点树。
自定义Trace
开辟者可以根据业务需求,使用hiTraceMeter进行自定义Trace办理跟踪,现在支持ArkTS和Native,详细使用细节可参考下方链接:
性能办理跟踪开辟指导(ArkTS)
性能办理跟踪开辟指导(Native)
添加自定义Trace后,可在SmartPerf-Host调试工具上查看,自定义Trace将以独立泳道的情势呈现在对应办理的历程下。
下图两条泳道使用了startTrace和finishTrace方法,表示步伐运行过程中,指定标签从调用startTrace到调用finishTrace的耗时统计。图中记载了CUSTOM_TRACE_TAG_1和CUSTOM_TRACE_TAG_2两个标签,先后呈现了2个标签的耗时统计。
图12 自定义Trace示例
https://i-blog.csdnimg.cn/blog_migrate/9461cbd4c72120f8b4ffbb03c664ae42.png
下图两条泳道使用了TraceByValue方法,表示步伐运行过程中,指定Trace在对应时间段内的状态值,状态值寄义可按需传参,开辟者可以通过鼠标放置在对应数据块上,来查看详细的状态值。图中记载了CUSTOM_TRACE_TAG_2标签在赤色方框标识的时间段内,办理状态值为2001。
图13 自定义状态值示例
https://i-blog.csdnimg.cn/blog_migrate/48c7f35f4d0786795e88803040b7b730.png
性能办理原理
Trace的生成依赖了DFX子体系中的HiTrace组件,其中包罗的hiTraceMeter模块为开辟者提供体系性能办理接口,详细细节可参考下方链接:
HiTrace组件
hiTraceMeter模块
hiTraceMeter拥有两套开始和结束办理接口,实现对逻辑行为的耗时统计。由于耗时统计大多数以方法为单位,所以hiTraceMeter也提供了快速办理单个方法执行耗时的宏定义HITRACE_METER、HITRACE_METER_NAME、HITRACE_METER_FMT,使用它们,只需要在方法起始位置调用即可。这些宏定义依赖了方法内局部变量的生命周期,其原理是在方法开始时构造了一个办理实例,在实例构造函数中调用开始办理接口,当方法执行完毕,办理实例随着方法结束而执行析构,在实例析构函数中调用结束办理接口。
App中的办理示例
ArkUI框架子体系应用hiTraceMeter的例子,来源于ArkUI开辟框架源码。
以下代码对hiTraceMeter进行接口封装,其原理与HITRACE_METER等雷同,依赖方法内局部变量的生命周期实现快速办理。
// frameworks/base/log/ace_trace.h
#define ACE_SCOPED_TRACE(fmt, ...) AceScopedTrace aceScopedTrace(fmt, ##__VA_ARGS__)
#define ACE_FUNCTION_TRACE() ACE_SCOPED_TRACE(__func__)
class ACE_FORCE_EXPORT AceScopedTrace final {
public:
explicit AceScopedTrace(const char* format, ...) __attribute__((__format__(printf, 2, 3)));
~AceScopedTrace();
ACE_DISALLOW_COPY_AND_MOVE(AceScopedTrace);
private:
bool traceEnabled_ { false };
};
以下代码是刷新视图同步变乱,包括记载帧信息、刷新任务、绘制渲染上下文、处置惩罚用户输入。在方法开头调用宏定义ACE_FUNCTION_TRACE,将函数名FlushVsync作为Trace名称记载下来,并记载函数开始时间,在函数结束时记载函数结束时间,得出执行耗时。
// frameworks/core/pipeline/pipeline_context.cpp
void PipelineContext::FlushVsync(uint64_t nanoTimestamp, uint32_t frameCount)
{
ACE_FUNCTION_TRACE();
// 此处省略方法内的其他业务逻辑
// ...
}
RS中的办理示例
图形子体系应用hiTraceMeter的例子,来源于图形子体系源码。
以下代码对hiTraceMeter进行接口封装。
// utils/log/rs_trace.h
#include "hitrace_meter.h"
#define ROSEN_TRACE_BEGIN(tag, name) StartTrace(tag, name)
#define RS_TRACE_BEGIN(name) ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, name)
#define ROSEN_TRACE_END(tag) FinishTrace(tag)
#define RS_TRACE_END() ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP)
#define RS_TRACE_NAME(name) HITRACE_METER_NAME(HITRACE_TAG_GRAPHIC_AGP, name)
#define RS_TRACE_NAME_FMT(fmt, ...) HITRACE_METER_FMT(HITRACE_TAG_GRAPHIC_AGP, fmt, ##__VA_ARGS__)
#define RS_ASYNC_TRACE_BEGIN(name, value) StartAsyncTrace(HITRACE_TAG_GRAPHIC_AGP, name, value)
#define RS_ASYNC_TRACE_END(name, value) FinishAsyncTrace(HITRACE_TAG_GRAPHIC_AGP, name, value)
#define RS_TRACE_INT(name, value) CountTrace(HITRACE_TAG_GRAPHIC_AGP, name, value)
#define RS_TRACE_FUNC() RS_TRACE_NAME(__func__)
以下代码在显示器画面绘制方法。在方法开头调用宏定义RS_TRACE_NAME,将函数名ProcessDisplayRenderNode与对应的显示器id组合后,作为Trace名称记载下来,同时由于其本质是使用了快速办理单个方法的宏定义HITRACE_METER_NAME,于是只需要调用一次,即可收集到ProcessDisplayRenderNode函数的执行起尽头时间,得出执行耗时。
// rosen/modules/render_service/core/pipeline/rs_surface_capture_task.cpp
void RSSurfaceCaptureVisitor::ProcessDisplayRenderNode(RSDisplayRenderNode &node)
{
RS_TRACE_NAME("RSSurfaceCaptureVisitor::ProcessDisplayRenderNode:" +
std::to_string(node.GetId()));
// 此处省略方法内的其他业务逻辑
// ...
}
为了能让各人更好的学习鸿蒙(HarmonyOS NEXT)开辟技术,这边特意整理了《鸿蒙开辟学习手册》(共计890页),希望对各人有所帮助:https://qr21.cn/FV7h05
《鸿蒙开辟学习手册》:
如何快速入门:https://qr21.cn/FV7h05
[*]根本概念
[*]构建第一个ArkTS应用
[*]……
https://i-blog.csdnimg.cn/blog_migrate/da60b8f42181f7f9d7276a5749b952ba.png
开辟底子知识:https://qr21.cn/FV7h05
[*]应用底子知识
[*]配置文件
[*]应用数据管理
[*]应用安全管理
[*]应用隐私掩护
[*]三方应用调用管控机制
[*]资源分类与访问
[*]学习ArkTS语言
[*]……
https://i-blog.csdnimg.cn/blog_migrate/399acf2dd0083550d646e7127303d2e6.png
基于ArkTS 开辟:https://qr21.cn/FV7h05
[*]Ability开辟
[*]UI开辟
[*]公共变乱与通知
[*]窗口管理
[*]媒体
[*]安全
[*]网络与链接
[*]电话服务
[*]数据管理
[*]后台任务(Background Task)管理
[*]装备管理
[*]装备使用信息统计
[*]DFX
[*]国际化开辟
[*]折叠屏系列
[*]……
https://i-blog.csdnimg.cn/blog_migrate/52f2adda9e3bb6ab141e75301e2d7452.png
鸿蒙开辟口试真题(含参考答案):https://qr18.cn/F781PH
https://i-blog.csdnimg.cn/blog_migrate/c1f8cde7842aea94dbfea28766d56bda.png
鸿蒙开辟口试大盘集篇(共计319页):https://qr18.cn/F781PH
1.项目开辟必备口试题
2.性能优化方向
3.架构方向
4.鸿蒙开辟体系底层方向
5.鸿蒙音视频开辟方向
6.鸿蒙车载开辟方向
7.鸿蒙南向开辟方向
https://i-blog.csdnimg.cn/blog_migrate/54d2bb79318ad66bb11729cd0e18fa32.png
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]