本原创文章帖发布在华为开发者联盟社区,欢迎开发者前往访问评论交流,更多与该内容相关讨论,请点击原帖查看:

RN 性能调优之基础定界与 trace 分析介绍-华为开发者话题 | 华为开发者联盟

本文是 RN 性能定界定位的基础篇,面向 RNOH 应用开发者,介绍三方面内容:

   1. RNOH 的线程模型,以及一次页面更新经过的环节;

   2. trace 的采集与查看方法;

   3. RNOH 在 trace 中输出的关键标记及其含义。

       完成基础篇阅读后,如需深入定位具体问题,请继续阅读:

          • RN 性能调优之滑动卡顿问题介绍

          • RN 性能调优之点击响应和完成时延分析介绍

名词说明

       本文会反复用到以下名词,含义见下表,阅读过程中可随时查阅:

名词

解释

RNInstance

一个 RN 运行实例(含 JS 运行时和渲染环境),一个应用可以同时存在多个

Surface

由 RN 渲染的独立显示区域,通常对应一个页面、弹层或卡片

Fabric

React Native 新架构的渲染系统,负责影子树的创建、布局、对比和挂载

影子树(ShadowTree)

组件树在ArkUI侧的中间表示,布局与对比先在影子树上完成,差异再应用到真实界面

mutations

新旧影子树对比后生成的变更指令列表(详见 §1.2)

TurboModule

JS 调用ArkUI能力的模块(网络、文件、传感器等)

ArkUI

鸿蒙UI 框架,RN 的组件最终由它创建和绘制

Render Service

系统合成服务,负责将各应用的画面合成到屏幕上

App Frame / RS Frame

性能工具中的两条帧泳道:应用产帧 / 系统合成帧

trace

性能分析记录,按时间轴展示各线程在各时间段的执行内容

1. RNOH 线程模型与渲染链路

1.1 四线程模型

       RNOH 内部有四类线程,各自承担不同职责:

线程

职责

QoS 优先级

trace 中如何识别

MAIN/UI

应用主线程(也是 UI 线程),每个应用进程只有一个。负责 ArkUI 组件生命周期管理(CREATE/UPDATE/INSERT/REMOVE/DELETE)、组件树管理、TurboModule 业务执行、交互事件处理

跟随系统主线程

主线程名不会被改成 RNOH_MAIN(复用主线程),需通过"应用主线程 + RNOH 调用栈"确认

JS

JS 运行时线程,通过 Hermes 执行 React 代码。负责 bundle 加载、ShadowTree 创建、Yoga 布局计算、新老树 diff 生成 mutations、交互事件处理。每个 RNInstance 绑定一个独立 JS 线程

QOS_USER_INTERACTIVE(用户交互级,与 UI 渲染同档的高优先级)

搜索线程名 RNOH_JS

BACKGROUND

实验特性,开启后会将 JS 线程的部分布局、ShadowTree 比较任务迁移到该线程,降低 JS 线程负荷。由于涉及复杂线程通信,稳定性有风险,正式商用版本不要开启

未单独设置

开启后搜索线程名 RNOH_BACKGROUND;默认不创建该线程

WORKER

TurboModule 工作线程,将 TurboModule 运行在独立线程,避免与主线程 UI 绘制竞争资源。需要在继承 RNAbility 后重写 getRNOHWorkerScriptUrl 方法配置

跟随 Worker 线程默认

搜索 WorkerThread

       线程之间的通信方向:JS → Main 通过任务投递进入主线程事件循环;Main → JS 通过消息队列投递到 JS 线程。

       下图是一次典型操作中各线程的 trace 示例(线程号 53130 为 MAIN 线程,53214/53216 为两个 JS 线程实例):

1.2 渲染三阶段

       RNOH 的渲染流程继承自 React Native 新架构,分为三个阶段,分布在不同线程执行:

          • 渲染(Render):在 JS 线程中,React 执行业务代码创建 React 元素树(React Element Tree),然后通过 JSI 在 C++ 中创建 React 影子树(React Shadow Tree)。

          • 提交(Commit):影子树创建完成后,通过 Yoga 引擎进行布局计算,然后与上一次的影子树对比,生成变更指令列表(mutations)。

          • 挂载(Mount):mutations 被提交到主线程,转化为 ArkUI 组件树操作(CREATE/INSERT/UPDATE/REMOVE/DELETE),最终由 ArkUI 完成绘制和合成。

       下图以一次点击跳转到 1500 个 Text 组件页面为例,展示 trace 中的渲染三阶段实例:

       关于 mutations:mutations 是 Fabric 对新旧影子树做 diff 后生成的变更指令列表,每条指令描述一个组件操作(如创建 tag=42 的 Text 组件、更新 tag=10 的 View 属性、将 tag=5 插入到 tag=3 下)。主线程逐条执行这些指令来完成界面更新。

1.3 性能链路

       一次 RN 页面更新通常跨越以下环节。图中标注了 ①~⑥ 序号,与 §1.2"渲染三阶段 trace 实例"图(点击跳转到 1500 个 Text 组件页面)中的标注一一对应:

       各环节与图中序号、渲染三阶段、trace 标记的对应关系(trace 标记的含义见 §3):

执行环节

图中序号

渲染三阶段

所在线程

对应 trace 标记

手指点击、输入分发到 RN Surface

①

输入

RNOH_MAIN

#RNOH::ArkUISurface::onTouchEvent::DOWN/UP

JS 线程响应事件、执行 React 业务代码

②

渲染(Render)

RNOH_JS

#RNOH::MessageQueueThread::runOnQueue

通过 JSI 创建影子树

③

渲染(Render)

RNOH_JS

ReactMarker::FABRIC_COMMIT、cloneNode 等节点树操作

Yoga 布局计算

④

提交(Commit)

RNOH_JS

ReactMarker::FABRIC_LAYOUT、#RNOH::TextMeasurer::measure

新旧影子树对比,生成 mutations

⑤

提交(Commit)

RNOH_JS

ReactMarker::FABRIC_DIFF

mutations 送主线程启动渲染

⑥

提交(Commit)收尾

RNOH_JS → RNOH_MAIN

ReactMarker::FABRIC_FINISH_TRANSACTION(主线程开始处理事务)

mutations 挂载(CREATE/INSERT/UPDATE/REMOVE/DELETE)

-

挂载(Mount)

RNOH_MAIN

ReactMarker::FABRIC_BATCH_EXECUTION、#RNOH::MountingManager::doMount

ArkUI 布局/绘制、Render Service 合成上屏

-

挂载(Mount)之后

RNOH_MAIN / Render Service

FlushVsync 等系统 trace

       图中 ①~⑥ 的逐步说明也可参考渲染三阶段。

       RNOH_MAIN 就是应用主线程,RNOH 在其事件循环上注册回调,不创建新线程。在 trace 中通常需要通过应用主线程身份和 RNOH 调用栈确认,不能只按线程名匹配。RNOH_JS 是独立线程,但线程名也只是一条线索,最终仍需用调用栈和时间关系确认。

1.4 指标定义

       在分析 trace 前,需要统一以下指标的定义和测量方法。

卡顿和丢帧

       帧预算由问题窗口内的实际目标刷新率决定:

单帧预算(ms) = 1000 / 目标刷新率(Hz)

       例如,60 Hz、90 Hz、120 Hz 对应的理论预算约为 16.7 ms、11.1 ms、8.3 ms。设备可能动态切换刷新率,因此不能固定使用 16.7 ms,也不能仅用"FPS 低于 60"判断丢帧。应在 DevEco Profiler 中读取 Display、H:PreferredFrameRate 或等价信息,并结合 App Frame、RS Frame 判断。

响应时延

       响应时延描述输入事件到首个可见反馈之间的时间,其数值与起止点的选取直接相关:

          • 起点:系统工具通常取点击事件上报时刻,而不是物理触屏时刻;

          • 终点:通常取首个响应帧上屏。

       因此报告中必须记录所用工具和起止点定义。不同工具、不同版本对起止点的取法可能不同,不能把 RNOH 内部的触摸 trace 直接当作端到端响应时延。

完成时延

       完成时延描述操作开始到目标页面达到工具所定义"完成状态"的时间。不同测试工具可能以页面切换后的目标帧、动画结束或占位内容加载完成作为终点。业务还可能把"关键内容可见"定义为完成。测试基线、问题复现和优化前后对比必须使用同一终点。

       RNOH 的 CONTENT_APPEARED 表示根节点挂载到 ArkUI 内容容器(NodeContent);ROOT_VIEW_TTI 计算为从 APP_STARTUP_START 到 CONTENT_APPEARED 的耗时。它们是 RNOH 内部阶段标记,不证明像素已经上屏、页面内容完整,也不证明页面已经可交互。

    HarmonyOS 指标和工具限制请以性能分析与HiSmartPerf Device 性能使用指导为准。

2. trace 采集与查看

2.1 RNOH 两类 trace 点

       RNOH 在 trace 中留下的标记分为两类,粒度和用途完全不同:

维度

RNOHMarker(阶段级)

TraceSection(函数级)

trace 中搜索

ReactMarker::

#RNOH::

粒度

标记一个完整阶段的起止(如 Bundle 加载开始→结束)

标记单个函数或代码块的执行区间

典型时间跨度

大(数十毫秒到数秒)

小(微秒到毫秒级)

是否跨线程

可以(起点和终点可以在不同线程)

不可以(限同一线程内)

编译开关

WITH_HITRACE_REACT_MARKER

WITH_HITRACE_SYSTRACE

       两类标记的用途可以这样区分:分析"某个阶段花了多久"(如 Bundle 加载、启动)时查看 RNOHMarker,分析"哪个函数耗时"(如挂载、触摸分发)时查看 TraceSection。两类标记的具体含义见 §3。

2.2 开启 trace 编译开关

       两个编译宏默认关闭,需要手动开启:

编译宏

控制的 trace 类型

默认值

WITH_HITRACE_REACT_MARKER

ReactMarker::* 异步 trace(阶段标记)

OFF

WITH_HITRACE_SYSTRACE

#RNOH::* 同步 trace(函数标记)+ JS 侧 nativeTrace* API

OFF

    注意:开关需要在 CMake 引入 RNOH 目录(add_subdirectory)之前设置,才能对 RNOH 源码生效。

三种开启方式(任选其一,普通应用工程用方式 A 即可):

       方式 A:应用 entry 的 CMakeLists.txt 中设置(应用工程推荐,模板默认已开启)

set(WITH_HITRACE_SYSTRACE ON)
set(WITH_HITRACE_REACT_MARKER ON)

       方式 B:build-profile.json5 的 externalNativeOptions.arguments 中传递(不想改 CMakeLists.txt 时使用,配置随工程走)

"externalNativeOptions": {
  "arguments": [
  "-DWITH_HITRACE_SYSTRACE=ON",
  "-DWITH_HITRACE_REACT_MARKER=ON"
  ]
}

       方式 C:第三方工程 CMakeLists.txt 中用 add_compile_definitions(三方库/HAR 场景)

set(WITH_HITRACE_SYSTRACE 1)
add_compile_definitions(WITH_HITRACE_SYSTRACE)
set(WITH_HITRACE_REACT_MARKER 1)
add_compile_definitions(WITH_HITRACE_REACT_MARKER)

2.3 抓取 trace

       正式采集前,先固定测试条件:

          • 使用 release 构建和 release bundle,关闭 Metro、HMR、Inspector 和高频调试日志。

          • 固定设备型号、HarmonyOS 版本、RNOH 版本、刷新率策略、网络和测试数据。

          • 记录温度、是否充电、电量和电源模式;先预热,再重复相同操作至少 5 次。

          • 性能浮层只用于快速观察,正式测量时关闭浮层、录屏和其他可能改变负载的调试功能。

          • 同时保留基线数据与问题数据,避免只分析单次偶发 trace。

       使用 DevEco Profiler(深度录制)抓取 trace 的详细步骤(操作界面以 DevEco Studio 实际版本为准):

       第 1 步:创建分析会话

          1. 使用 release 或 debugOptimized 构建模式安装应用,并连接设备;

          2. 打开 DevEco Studio → Profiler,在设备列表中选择设备,在进程列表中选择要调测的应用(可以是正在运行的应用,也可以是已安装但未启动的应用);

          3. 在新建任务区域选择场景模板,单击 Create Session。常用模板与适用问题:

模板

适用问题

Frame

卡顿丢帧:采集 App Frame、RS Frame 和渲染相关数据,适合滑动卡顿类问题

Time

函数执行效率:深度录制函数调用栈和每帧耗时,展现 ArkTS 到 Native 的跨语言调用栈,适合启动、时延类问题

Launch

启动耗时:分析启动周期各阶段耗时和核心线程运行情况

       第 2 步:配置录制选项和泳道

          • 录制详情区域的工具栏上有多个配置图标,鼠标悬停可查看功能提示,按需添加录制选项;

          • 各泳道区域有下拉选项,可调整该泳道的录制功能;录制前可指定要录制的泳道。

       对 RNOH 应用,建议泳道至少包含:**App Frame、RS Frame、应用主线程、RNOH_JS、Render Service**。

       第 3 步:启动录制并复现问题

       单击任务窗口左上角(或左侧任务列表)的启动按钮,等待任务状态由 "initializing" 变为 "recording" 后,在设备上操作应用、复现性能问题。录制过程中不能再操作其他模板;如需更换模板,需结束本次录制后重新创建会话。

       第 4 步:停止录制并等待解析

       复现完成后单击停止按钮,泳道任务状态由 "analyzing" 变为 "rendering" 时解析结束,右侧详情区域显示分析结果;数据量较大时解析需要时间,请耐心等待。若 ArkTS Callstack、Callstack 等泳道显示 No Data,可将光标悬浮在泳道名称处的三角告警图标上查看原因。

       第 5 步:保存 trace 文件

       保存 .htrace 文件,便于后续查询和复核。

       抓取完成后,即可在泳道中查看应用主线程、RNOH_JS 等线程的活动区间(同 §1.1 的示例):

       更详细的 trace 抓取操作(包括命令行方式、采样配置、trace 文件解析等)请参考 HarmonyOS 官方文档Deep Recording 使用指导,其支持的全部场景模板(Snapshot、Allocation、ArkUI、Energy、ArkWeb、Network、Concurrency、GPU、CPU 等)也以该文档为准。

       采集清单:每个问题建议至少提供 — 复现步骤和问题时间点、应用包类型和版本信息、DevEco Profiler trace 文件、App Frame/RS Frame/主线程/RNOH_JS 泳道数据、操作录屏或截图、基线版本与问题版本对照数据。

2.4 查看 trace 的技巧

       • 定位 JS 线程:在 trace 中搜索线程名 RNOH_JS。

       • 定位阶段标记:搜索 ReactMarker::,可快速定位到启动、Bundle 加载、Fabric 提交等阶段的起止。

       • 定位函数执行:搜索 #RNOH::,可定位到具体的函数调用区间(如挂载、触摸分发、TurboModule 调用)。

       • 定位异常窗口:先看 App Frame 和 RS Frame 泳道中的红色异常帧,然后缩放到该时间区间,查看该窗口内各线程的活动。

       • 确认线程身份:RNOH_MAIN 不在 trace 中直接显示此名称(主线程复用应用自身线程名),需通过应用主线程 + #RNOH::TaskExecutor::runTask 等 TraceSection 调用栈确认。

3. RNOH 关键 trace 点解读

3.1 ReactMarker(阶段级异步 trace)

       React Marker 的作用是监听 RN 应用生命周期中的关键事件并打点,方便开发者分析性能问题。打点覆盖了从启动到渲染的完整时间线,可归纳为四类:

          • 启动初始化:运行时/实例/上下文创建、ArkUI模块注册;

          • Bundle 加载:下载、格式转换、分段注册、执行;

          • 页面内容显示:内容出现、Surface 前后台切换;

          • Fabric 渲染:提交、布局、diff、挂载。

       该类 trace 需要开启 WITH_HITRACE_REACT_MARKER 编译开关(见 §2.2)。

       查看 ReactMarker 的 trace 时,需要注意标记名与 trace 显示名之间的映射关系:

          • 标记大多成对出现,带 _START/_STOP(或 _END)后缀,例如 RUN_JS_BUNDLE_START 和 RUN_JS_BUNDLE_STOP;

          • 成对的两个标记在 trace 中显示为同一个名称,如 ReactMarker::RUN_JS_BUNDLE(部分工具显示为 H:ReactMarker::RUN_JS_BUNDLE::Tag::<tag>),_START/_STOP 后缀不体现在名称中,而是分别对应该异步 trace 时间条的开始和结束;

          • 带 tag 的标记会在名称后追加 ::Tag::<tag>;

          • 个别标记名与 trace 显示名不同,如 JS_BUNDLE_STRING_CONVERT_START/STOP 在 trace 中显示为 ReactMarker::loadApplicationScript_StringConvert。

       以下是在 trace 工具中查看 ReactMarker 的示例:

       图中红色框 1 代表的是应用进程,红色框 2 代表的是应用运行过程中打印出来的 React Marker 信息。

以下按阶段分组列出性能定界中最常用的 ReactMarker。在 trace 工具中搜索 ReactMarker:: 即可找到这些标记。

启动与运行时初始化

RNOHMarkerId

trace 显示名

含义

可衡量

不可衡量

APP_STARTUP_START / APP_STARTUP_STOP

ReactMarker::APP_STARTUP

RNOH 应用启动总阶段

启动阶段的总耗时范围

各子阶段细节需看其他 marker

INIT_REACT_RUNTIME_START / INIT_REACT_RUNTIME_STOP

ReactMarker::INIT_REACT_RUNTIME

React 运行时初始化

运行时创建的起止时间

JS 引擎是否完全就绪

REACT_INSTANCE_INIT_START / REACT_INSTANCE_INIT_STOP

ReactMarker::REACT_INSTANCE_INIT

RNInstance 实例创建

实例创建的起止时间

实例创建后是否可立即渲染

CREATE_REACT_CONTEXT_START / CREATE_REACT_CONTEXT_STOP

ReactMarker::CREATE_REACT_CONTEXT

React 上下文创建

上下文创建耗时

Bundle 是否已加载

Bundle 加载

RNOHMarkerId

trace 显示名

含义

可衡量

不可衡量

RUN_JS_BUNDLE_START / RUN_JS_BUNDLE_STOP

ReactMarker::RUN_JS_BUNDLE

JS Bundle 执行阶段

Bundle 从开始执行到执行完成的时间

Bundle 下载时间、业务页面首帧时间

JS_BUNDLE_STRING_CONVERT_START / JS_BUNDLE_STRING_CONVERT_STOP

ReactMarker::loadApplicationScript_StringConvert

Bundle 字符串转换

Bundle 数据格式转换耗时

Bundle 解析和执行时间

REGISTER_JS_SEGMENT_START / REGISTER_JS_SEGMENT_STOP

ReactMarker::REGISTER_JS_SEGMENT

JS 分段注册

多 Bundle 分段注册的耗时

分段注册后业务是否就绪

DOWNLOAD_START / DOWNLOAD_END

ReactMarker::DOWNLOAD

JS Bundle 下载阶段

Bundle 下载起止时间

下载后的缓存命中情况

REACT_BRIDGE_LOADING_START / REACT_BRIDGE_LOADING_END

ReactMarker::REACT_BRIDGE_LOADING

React 桥接模块加载阶段

整体加载阶段耗时

加载内部各步骤的细节

Native 模块

RNOHMarkerId

trace 显示名

含义

可衡量

不可衡量

NATIVE_MODULE_SETUP_START / NATIVE_MODULE_SETUP_STOP

ReactMarker::NATIVE_MODULE_SETUP

ArkUI模块注册和初始化

所有ArkUI模块注册的总耗时

单个模块的初始化时间

INITIALIZE_MODULE_START / INITIALIZE_MODULE_END

ReactMarker::INITIALIZE_MODULE

单个模块初始化

每个模块的初始化耗时

模块初始化后的首次调用时间

PROCESS_PACKAGES_START / PROCESS_PACKAGES_STOP

ReactMarker::PROCESS_PACKAGES

处理所有 Package

Package 处理的起止时间

每个 Package 内部的模块详情

CREATE_UI_MANAGER_MODULE_START / CREATE_UI_MANAGER_MODULE_END

ReactMarker::CREATE_UI_MANAGER_MODULE

创建 UI_Manager 模块

UIManager 模块创建的起止时间

模块创建后的渲染表现

CREATE_MODULE_START / CREATE_MODULE_END

ReactMarker::CREATE_MODULE

创建单个 React 模块

单个模块创建的耗时

模块创建后的调用情况

PROCESS_CORE_REACT_PACKAGE_START / PROCESS_CORE_REACT_PACKAGE_END

ReactMarker::PROCESS_CORE_REACT_PACKAGE

React Native 核心包加载

核心包处理的起止时间

核心包内部各模块的细节

Surface 生命周期

RNOHMarkerId

trace 显示名

含义

可衡量

不可衡量

CONTENT_APPEARED

ReactMarker::CONTENT_APPEARED

应用程序中的内容已成功渲染并显示给用户(根节点挂载到 ArkUI 内容容器 NodeContent)

根节点已挂载的时间点

像素已上屏、关键内容可见或可交互

ON_HOST_RESUME_START / ON_HOST_RESUME_END

ReactMarker::ON_HOST_RESUME

Surface 恢复(可见)

Surface 进入可见状态的时间

Surface 首帧已渲染完成

ON_HOST_PAUSE_START / ON_HOST_PAUSE_END

ReactMarker::ON_HOST_PAUSE

Surface 暂停(不可见)

Surface 进入不可见状态的时间

Surface 资源已释放

Fabric(UI 渲染)

RNOHMarkerId

trace 显示名

含义

可衡量

不可衡量

FABRIC_COMMIT_START / FABRIC_COMMIT_END

ReactMarker::FABRIC_COMMIT

Fabric 渲染引擎的提交

React 一次提交的起止时间

提交后是否立即触发挂载

FABRIC_DIFF_START / FABRIC_DIFF_END

ReactMarker::FABRIC_DIFF

新旧影子树对比(diff)

diff 计算的耗时

diff 结果的 mutation 数量

FABRIC_LAYOUT_START / FABRIC_LAYOUT_END

ReactMarker::FABRIC_LAYOUT

布局计算(Yoga)

布局计算的耗时

文本测量的内部细节

FABRIC_FINISH_TRANSACTION_START / FABRIC_FINISH_TRANSACTION_END

ReactMarker::FABRIC_FINISH_TRANSACTION

Fabric 事务完成

整个事务从提交到完成的耗时

挂载是否已在主线程执行完毕

FABRIC_BATCH_EXECUTION_START / FABRIC_BATCH_EXECUTION_END

ReactMarker::FABRIC_BATCH_EXECUTION

mutations 批量挂载执行

一批 mutations 在主线程执行的耗时

页面内容已完整渲染

FABRIC_UPDATE_UI_MAIN_THREAD_START / FABRIC_UPDATE_UI_MAIN_THREAD_END

ReactMarker::FABRIC_UPDATE_UI_MAIN_THREAD

主线程同步更新组件属性

在主线程上同步更新单个组件属性的执行区间(避免跨线程延迟)

所有 Fabric transaction 的主线程挂载总时长

其他

RNOHMarkerId

trace 显示名

含义

可衡量

不可衡量

CHANGE_THREAD_PRIORITY

ReactMarker::CHANGE_THREAD_PRIORITY

线程 QoS 优先级变更

线程优先级调整的时间点和目标值

优先级变更后的实际调度效果

       提示:以上仅列出常用标记。完整的 56 个 ReactMarker 标记定义见源码 RNOH/Performance/RNOHMarker.h;其中部分标记(如 INIT_JS_RUNTIME、TURBO_MODULE_SETUP)不会输出到 trace,仅供 Marker 监听器(日志、性能指标等)使用。React Marker 的 trace 打点操作步骤,以及通过自定义 TurboModule 监听 React Marker 事件的实现方式,参见性能调优的 React Marker 介绍。

3.2 TraceSection(函数级同步 trace)

       以下列出性能定界中最常用的 TraceSection 标记,在 trace 工具中搜索 #RNOH:: 即可找到。这些标记在 trace 中显示为连续的区间切片,可以直接看到函数的执行时长。

触摸与事件

trace 字符串

含义

所属线程

#RNOH::ArkUISurface::onTouchEvent::DOWN

触摸按下事件进入 RN Surface

RNOH_MAIN

#RNOH::ArkUISurface::onTouchEvent::UP

触摸抬起事件进入 RN Surface

RNOH_MAIN

#RNOH::ArkUISurface::onTouchEvent::MOVE

触摸移动事件进入 RN Surface

RNOH_MAIN

#RNOH::Subject::notifyObservers

事件分发给观察者

各线程

挂载(Mount)

trace 字符串

含义

所属线程

#RNOH::MountingManager::doMount

开始执行一批 mutations 挂载

RNOH_MAIN

#RNOH::MountingManager::didMount <count>

挂载完成,count 为 mutation 数量

RNOH_MAIN

#RNOH::MountingManager::dispatchCommand

分发组件命令(如 scrollTo)

RNOH_MAIN

Scheduler(Fabric 调度)

trace 字符串

含义

所属线程

#RNOH::SchedulerDelegate::performTransaction

处理一次 Fabric 事务

RNOH_MAIN

#RNOH::SchedulerDelegate::onNextFrame

下一帧调度

RNOH_MAIN

#RNOH::SchedulerDelegate::didMount

Scheduler 挂载完成回调

RNOH_MAIN

#RNOH::SchedulerDelegate::handleMutation_parallel

并行处理 mutations

RNOH_MAIN

#RNOH::SchedulerDelegate::parallelCreateWait

并行创建组件等待

RNOH_MAIN

#RNOH::SchedulerDelegate::otherCreate

串行创建组件

RNOH_MAIN

TaskExecutor(任务调度)

trace 字符串

含义

所属线程

#RNOH::TaskExecutor::runTask

TaskExecutor 执行一个任务

各线程

#RNOH::TaskExecutor::runningTask

正在执行的任务(含任务 ID)

各线程

#RNOH::TaskRunner::task

事件循环执行单个任务

RNOH_MAIN / RNOH_JS

实例与通信

trace 字符串

含义

所属线程

#RNOH::RNInstanceCAPI::synchronouslyUpdateViewOnUIThread

主线程同步更新组件属性(避免跨线程延迟)

RNOH_MAIN

#RNOH::MessageQueueThread::runOnQueue

向 JS 队列投递任务

RNOH_JS

#RNOH::RNInstanceInternal::callJSFunction

调用 JS 函数

RNOH_JS

#RNOH::ArkTSTurboModule::call (<name>::<methodName>)

ArkTS TurboModule 调用(含模块名和方法名)

RNOH_MAIN

#RNOH::TextMeasurer::measure

文本测量

RNOH_JS

       提示:以上仅列出常用标记。完整的 TraceSection 字符串可通过在源码中搜索 TraceSection s( 或 SystraceSection s( 获取。

3.3 按 trace 时间轴分析一次点击响应

       单个 trace 切片只能说明某个环节的执行区间,定位性能问题时需要按时间顺序将多个切片串联,还原从输入到上屏的完整执行过程。

       以一次点击响应为例,完整的 trace 点序列如下(两条竖线代表两个线程,从上到下即时间顺序):

       依据各切片之间的时间间隔,可以定位耗时集中在哪个环节:

          • 如果 onTouchEvent::DOWN 到 runOnQueue 之间很长 → 主线程调度或触摸分发慢;

          • 如果 FABRIC_COMMIT 到 FABRIC_FINISH_TRANSACTION 之间很长 → JS 侧 React render/layout/diff 慢;

          • 如果 FABRIC_BATCH_EXECUTION 到 didMount 之间很长 → 主线程挂载慢;

          • 如果 didMount 到首个响应帧上屏之间很长 → ArkUI 布局/绘制或 Render Service 合成慢。

4. 定界方法与常见错误

4.1 责任域划分

       一次有效定界至少给出以下结论之一:

责任域

典型证据

后续处理方

HarmonyOS 系统或 Render Service

RS Frame 异常,应用按时提交;同设备多个应用同时劣化

系统/图形侧

ArkUI宿主或 ArkTS 业务

应用主线程被非 RNOH 的 ArkTS、I/O、图片处理等任务占满

ArkUI业务侧

RN JS 业务

RNOH_JS 长任务、频繁 React 提交或事件回调过重

RN 业务侧

三方 RN 组件

异常调用栈集中在三方组件,移除或降级后恢复

三方组件维护方

RNOH 框架

异常窗口关键路径落在 RNOH 实现,最小示例仍复现,且版本对比显示框架回归

RNOH 框架侧

证据不足

只有主观现象、平均 FPS 或单个长切片,无法覆盖从输入到上屏的完整过程

补充采集

       "使用了 RNOH"不等于"RNOH 框架问题"。例如,大量业务组件一次性提交后,挂载耗时发生在 RNOH 主线程,但如果耗时随业务节点数增长且最小 RNOH 页面正常,通常应先归为 RN 业务负载或组件使用问题,而不是框架缺陷。

4.2 定界前准备

       • 确认 trace 编译开关已开启:检查 WITH_HITRACE_REACT_MARKER 和 WITH_HITRACE_SYSTRACE 是否在构建配置中设置为 ON(详见 §2.2)。

       • 测试条件的固定(release 构建、设备/版本/数据固定、预热重复、基线数据与问题数据等)见 §2.3 开头的采集要求,此处不再重复。

4.3 三类问题的快速判定

滑动卡顿丢帧

       详细方法见RN性能调优之滑动卡顿问题介绍。

响应时延

       按"输入锚点 → 首个响应帧上屏"分段测量,定位耗时最长的一段:

输入 → 应用触摸入口 慢 → 系统输入分发 / 主线程调度
触摸分发完成 → JS 事件 晚 → RNOH_JS 排队或阻塞
JS 提交 → 首个变化上屏 晚 → 挂载、ArkUI 或 RS
全程无任何视觉变化 → 先检查业务是否设计了即时反馈

完成时延

       先定义"完成"终点,再分段定位责任方:

数据/网络未就绪 → 业务或服务侧
JS render/commit 晚 → RN JS 业务或三方组件
mutations 已产生但挂载晚 → RNOH_MAIN、节点规模或同步阻塞
应用按时提交但上屏晚 → ArkUI/RS/系统侧
仅 CONTENT_APPEARED 早 → 不能据此判定页面已经完成

       详细方法见RN性能调优之点击响应和完成时延分析介绍。

4.4 常见错误定界

       • 只看 JS FPS/UI FPS 就分责:浮层是采样指标,适合发现趋势,不足以证明一次异常的关键路径。

       • 只看一个超过帧预算的函数:它可能不在异常帧、可能运行在非关键线程,也可能被后续流水线吸收。

       • 应用和 RS 同时红就判系统问题:应用晚提交也会向 RS 传导,必须看最早异常点。

       • RNOH 调用栈长就判框架问题:需要结合输入规模、最小示例和版本对照。

       • **把 CONTENT_APPEARED 当完成时延终点**:该标记早于实际上屏和业务内容完整。

       • 用 debug 数据和 release 基线比较:构建、日志、Inspector 和 bundle 差异会破坏对照条件。

       • 把平均值当全部结论:平均 FPS 会掩盖最大连续丢帧,平均时延会掩盖 P95/P99 尖峰。

相关文档

       • RN 性能调优之滑动卡顿问题介绍(系列第三篇)

       • RN 性能调优之点击响应和完成时延分析介绍(系列第二篇)

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐