1e41f4b71Sopenharmony_ci# Using SmartPerf-Host to Analyze Application Performance 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ci## Overview 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ciSmartperf-Host is an intuitive performance and power optimization tool that offers in-depth data mining and fine-grained data visualization. In this tool, you can gain visibility into a multitude of metrics in terms of CPU scheduling, frequency, process and thread time slices, heap memory, frame rate, and more, in swimlanes. Better yet, you can analyze the collected data intuitively on the GUI. This tool provides five analysis templates: frame rate analysis, CPU/thread scheduling analysis, application startup analysis, task pool analysis, and animation analysis. For details about how to use the tool, see [Smartperf-Host User Guide](../../device-dev/device-test/smartperf-host.md). 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ciThis document provides some performance analysis examples to describe how to use the frame rate analysis and application startup analysis templates to collect and analyze performance data and identify areas of improvement. 8e41f4b71Sopenharmony_ci 9e41f4b71Sopenharmony_ci## Deployment 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ciBefore using SmartPerf-Host, deploy it on your local device. Then you can access SmartPerf-Host at **https://[*Device IP address*]:9000/application/**, as shown in the following figure. 12e41f4b71Sopenharmony_ci 13e41f4b71Sopenharmony_ci**Figure 1** Local deployment access page 14e41f4b71Sopenharmony_ci 15e41f4b71Sopenharmony_ci 16e41f4b71Sopenharmony_ci 17e41f4b71Sopenharmony_ci## Performance Analysis 18e41f4b71Sopenharmony_ci 19e41f4b71Sopenharmony_ci### FrameTimeline: Frame Rate Analysis 20e41f4b71Sopenharmony_ci 21e41f4b71Sopenharmony_ciThe FrameTimeline feature allows you to record the rendering data of each frame, automatically identify frame freezing, and gain system trace information in the same period. 22e41f4b71Sopenharmony_ci 23e41f4b71Sopenharmony_ci#### Example 24e41f4b71Sopenharmony_ci 25e41f4b71Sopenharmony_ciIn this example, the **\<Grid>** component is used to implement a grid layout. Frame freezing or frame loss occurs during swiping on the application page. Let's see how the FrameTimeline feature works in this case. 26e41f4b71Sopenharmony_ci 27e41f4b71Sopenharmony_ci``` 28e41f4b71Sopenharmony_ci@Entry 29e41f4b71Sopenharmony_ci@Component 30e41f4b71Sopenharmony_cistruct Index { 31e41f4b71Sopenharmony_ci @State children: number[] = Array.from<undefined, number>(Array(2000).fill(undefined), (_v: undefined, k) => k); 32e41f4b71Sopenharmony_ci build() { 33e41f4b71Sopenharmony_ci Scroll() { 34e41f4b71Sopenharmony_ci Grid() { 35e41f4b71Sopenharmony_ci ForEach(this.children, (item: number) => { 36e41f4b71Sopenharmony_ci GridItem() { 37e41f4b71Sopenharmony_ci Stack() { 38e41f4b71Sopenharmony_ci Stack() { 39e41f4b71Sopenharmony_ci Stack() { 40e41f4b71Sopenharmony_ci Text(item.toString()) 41e41f4b71Sopenharmony_ci .fontSize(32) 42e41f4b71Sopenharmony_ci } 43e41f4b71Sopenharmony_ci } 44e41f4b71Sopenharmony_ci } 45e41f4b71Sopenharmony_ci } 46e41f4b71Sopenharmony_ci }, (item: number) => item.toString()) 47e41f4b71Sopenharmony_ci } 48e41f4b71Sopenharmony_ci .columnsTemplate('1fr 1fr 1fr 1fr') 49e41f4b71Sopenharmony_ci .columnsGap(0) 50e41f4b71Sopenharmony_ci .rowsGap(0) 51e41f4b71Sopenharmony_ci .size({ width: "100%", height: "100%" }) 52e41f4b71Sopenharmony_ci } 53e41f4b71Sopenharmony_ci } 54e41f4b71Sopenharmony_ci} 55e41f4b71Sopenharmony_ci``` 56e41f4b71Sopenharmony_ci 57e41f4b71Sopenharmony_ci#### Recording Data 58e41f4b71Sopenharmony_ci 59e41f4b71Sopenharmony_ciTo record data with FrameTimeline, perform the following steps: 60e41f4b71Sopenharmony_ci 61e41f4b71Sopenharmony_ci1. Choose **Record template** > **Trace template** and enable **FrameTimeline**. 62e41f4b71Sopenharmony_ci 63e41f4b71Sopenharmony_ci **Figure 2** Enabling the FrameTimeline frame 64e41f4b71Sopenharmony_ci 65e41f4b71Sopenharmony_ci  66e41f4b71Sopenharmony_ci 67e41f4b71Sopenharmony_ci2. Customize the recording settings. 68e41f4b71Sopenharmony_ci 69e41f4b71Sopenharmony_ci **Figure 3** Recording settings 70e41f4b71Sopenharmony_ci 71e41f4b71Sopenharmony_ci  72e41f4b71Sopenharmony_ci 73e41f4b71Sopenharmony_ci3. Click **Record** in the upper right corner to start recording. At the same time, interact with the test device to reproduce the frame loss or frame freezing. When the recording is complete, the page automatically loads the trace data. 74e41f4b71Sopenharmony_ci 75e41f4b71Sopenharmony_ci**NOTE** 76e41f4b71Sopenharmony_ci 77e41f4b71Sopenharmony_ci- During data recording and analysis, do not exit the application or power off the device. Otherwise, the analysis may fail. 78e41f4b71Sopenharmony_ci 79e41f4b71Sopenharmony_ci- After you click **Record**, if "please kill other hdc-server!" is displayed on the top of the web page, the HDC port of the device is in use. In this case, run **hdc kill** in the CLI and reconnect to the device to try again. 80e41f4b71Sopenharmony_ci 81e41f4b71Sopenharmony_ci#### Analyzing Data 82e41f4b71Sopenharmony_ci 83e41f4b71Sopenharmony_ciA complete rendering process is as follows: The application responds to the user input, completes UI drawing, and submits the UI drawing to Render Service, which then coordinates resources such as the GPU to complete rendering, synthesis, and display. During this process, frame freezing and subsequent frame loss may occur on both the application and Render Service sides. 84e41f4b71Sopenharmony_ci 85e41f4b71Sopenharmony_ciBased on the three groups of data shown in Figure 4, Figure 5, and Figure 6, you can quickly locate where frame loss occurs and complete preliminary demarcation. 86e41f4b71Sopenharmony_ci 87e41f4b71Sopenharmony_ci**Figure 4** Total time consumed by the UI and RenderService 88e41f4b71Sopenharmony_ci 89e41f4b71Sopenharmony_ci 90e41f4b71Sopenharmony_ci 91e41f4b71Sopenharmony_ci 92e41f4b71Sopenharmony_ci**Figure 5** Time consumed by the UI 93e41f4b71Sopenharmony_ci 94e41f4b71Sopenharmony_ci 95e41f4b71Sopenharmony_ci 96e41f4b71Sopenharmony_ci 97e41f4b71Sopenharmony_ci**Figure 6** Time consumed by RenderService 98e41f4b71Sopenharmony_ci 99e41f4b71Sopenharmony_ci 100e41f4b71Sopenharmony_ci 101e41f4b71Sopenharmony_ci- **Expected Timeline** represents the expected, ideal timeline, and **Actual Timeline** the actual timeline. 102e41f4b71Sopenharmony_ci 103e41f4b71Sopenharmony_ci- There are three types of frames in the timeline: Green frames are normal frames, orange frames are janky frames, and yellow frames are where the interaction between the application and Render Service is abnormal. 104e41f4b71Sopenharmony_ci 105e41f4b71Sopenharmony_ci- In the preceding figures, the length of each frame indicates the amount of time spent on the frame. 106e41f4b71Sopenharmony_ci 107e41f4b71Sopenharmony_ci- If the actual end time of a frame on the application or Render Service side is later than the expected deadline, it is considered as a janky frame. 108e41f4b71Sopenharmony_ci 109e41f4b71Sopenharmony_ci- If there are orange frames on the application side, check whether the processing logic of the UI thread is too complex or inefficient and whether resources are preempted by other tasks. 110e41f4b71Sopenharmony_ci 111e41f4b71Sopenharmony_ci- If there are orange frames on the Render Service side, check whether the GUI layout is too complex. You can use ArkUI Inspector and [HiDumper](../performance/performance-optimization-using-hidumper.md) to analyze and locate the fault. 112e41f4b71Sopenharmony_ci 113e41f4b71Sopenharmony_ciIn this example, as shown in Figure 5 and Figure 6, the frame freezing issue lies in the application side. Click a janky frame for detailed analysis. The associated frames are represented through lines, and the details of the frame are displayed under **Current Selection**, as shown in Figure 7. 114e41f4b71Sopenharmony_ci 115e41f4b71Sopenharmony_ci**Figure 7** Frame freezing in the application 116e41f4b71Sopenharmony_ci 117e41f4b71Sopenharmony_ci 118e41f4b71Sopenharmony_ci 119e41f4b71Sopenharmony_ci- **Duration** indicates the amount of time spent on the frame. 120e41f4b71Sopenharmony_ci 121e41f4b71Sopenharmony_ci- **Jank Type** indicates the janky frame type. **APP Deadline Missed** indicates that the janky frame occurs on the application side. 122e41f4b71Sopenharmony_ci 123e41f4b71Sopenharmony_ci- **FrameTimeLine flows Slice** indicates the associated frame in **FrameTimeLine**. 124e41f4b71Sopenharmony_ci 125e41f4b71Sopenharmony_ci- **Preceding flows Slice** indicates the associated frame in Render Service. 126e41f4b71Sopenharmony_ci 127e41f4b71Sopenharmony_ciIn the following figure that shows the expanded application lanes, there are two lanes with the same name and PID. The first lane indicates the thread usage, and the second lane indicates the call stack in the thread. Based on the trace data of the time corresponding to the janky frame, it can be discerned that the FlushLayoutTask, which re-measures and lays out items, is time consuming. A closer look reveals that Layout[Gird] takes the longest time. Therefore, it is safe to conclude that the frame freezing can be ascribed to the gird layout processing logic being too complex or inefficient. 128e41f4b71Sopenharmony_ci 129e41f4b71Sopenharmony_ci**Figure 8** Application layout drawing trace data 130e41f4b71Sopenharmony_ci 131e41f4b71Sopenharmony_ci 132e41f4b71Sopenharmony_ci 133e41f4b71Sopenharmony_ciAfter locating and analyzing the grid layout code segment, we can optimize the code as follows: Remove the redundant three-layer stack container, pre-convert the source data to the string type required the layout, and add the **cachedCount** parameter to the **\<Grid>** component to work with the **LazyForEach** syntax for pre-loading. Set **cachedCount** to the number of grid items that can be rendered on one screen. After the optimization, let's record data in the same way. As shown in Figure 9, no frame freezing or frame loss occurs during swiping. 134e41f4b71Sopenharmony_ci 135e41f4b71Sopenharmony_ci**Figure 9** FrameTimeline diagram after optimization 136e41f4b71Sopenharmony_ci 137e41f4b71Sopenharmony_ci 138e41f4b71Sopenharmony_ci 139e41f4b71Sopenharmony_ciThe code after optimization is as follows: 140e41f4b71Sopenharmony_ci 141e41f4b71Sopenharmony_ci``` 142e41f4b71Sopenharmony_ciclass MyDataSource implements IDataSource { // LazyForEach data source 143e41f4b71Sopenharmony_ci private list: string[] = []; 144e41f4b71Sopenharmony_ci 145e41f4b71Sopenharmony_ci constructor(list: string[]) { 146e41f4b71Sopenharmony_ci this.list = list; 147e41f4b71Sopenharmony_ci } 148e41f4b71Sopenharmony_ci 149e41f4b71Sopenharmony_ci totalCount(): number { 150e41f4b71Sopenharmony_ci return this.list.length; 151e41f4b71Sopenharmony_ci } 152e41f4b71Sopenharmony_ci 153e41f4b71Sopenharmony_ci getData(index: number): string { 154e41f4b71Sopenharmony_ci return this.list[index]; 155e41f4b71Sopenharmony_ci } 156e41f4b71Sopenharmony_ci 157e41f4b71Sopenharmony_ci registerDataChangeListener(_: DataChangeListener): void { 158e41f4b71Sopenharmony_ci } 159e41f4b71Sopenharmony_ci 160e41f4b71Sopenharmony_ci unregisterDataChangeListener(): void { 161e41f4b71Sopenharmony_ci } 162e41f4b71Sopenharmony_ci} 163e41f4b71Sopenharmony_ci@Entry 164e41f4b71Sopenharmony_ci@Component 165e41f4b71Sopenharmony_cistruct Index { 166e41f4b71Sopenharmony_ci @State children: string[] = Array.from<undefined, string>(Array(2000).fill(undefined), (_v: undefined, k) => k.toString()); 167e41f4b71Sopenharmony_ci @State data: MyDataSource = new MyDataSource(this.children) 168e41f4b71Sopenharmony_ci build() { 169e41f4b71Sopenharmony_ci Scroll() { 170e41f4b71Sopenharmony_ci Grid() { 171e41f4b71Sopenharmony_ci LazyForEach(this.data, (item: string) => { 172e41f4b71Sopenharmony_ci GridItem() { 173e41f4b71Sopenharmony_ci Text(item) 174e41f4b71Sopenharmony_ci .fontSize(32) 175e41f4b71Sopenharmony_ci } 176e41f4b71Sopenharmony_ci }, (item: string) => item) 177e41f4b71Sopenharmony_ci } 178e41f4b71Sopenharmony_ci .cachedCount(80) 179e41f4b71Sopenharmony_ci .columnsTemplate('1fr 1fr 1fr 1fr') 180e41f4b71Sopenharmony_ci .columnsGap(0) 181e41f4b71Sopenharmony_ci .rowsGap(0) 182e41f4b71Sopenharmony_ci .size({ width: "100%", height: "100%" }) 183e41f4b71Sopenharmony_ci } 184e41f4b71Sopenharmony_ci } 185e41f4b71Sopenharmony_ci} 186e41f4b71Sopenharmony_ci``` 187e41f4b71Sopenharmony_ci 188e41f4b71Sopenharmony_ci### AppStartup: Application Startup Analysis 189e41f4b71Sopenharmony_ci 190e41f4b71Sopenharmony_ciThe AppStartup feature provides the time consumption of each phase during application startup. With the provided data, you can discover which phase is slowing down your application startup and the time-consuming call stacks on the system side. 191e41f4b71Sopenharmony_ci 192e41f4b71Sopenharmony_ci#### Example 193e41f4b71Sopenharmony_ci 194e41f4b71Sopenharmony_ciThis example shows how the AppStartup feature works. 195e41f4b71Sopenharmony_ci 196e41f4b71Sopenharmony_ci``` 197e41f4b71Sopenharmony_ci@Entry 198e41f4b71Sopenharmony_ci@Component 199e41f4b71Sopenharmony_cistruct Index { 200e41f4b71Sopenharmony_ci @State private text: string = "hello world"; 201e41f4b71Sopenharmony_ci private count: number = 0; 202e41f4b71Sopenharmony_ci 203e41f4b71Sopenharmony_ci aboutToAppear() { 204e41f4b71Sopenharmony_ci this.computeTask(); 205e41f4b71Sopenharmony_ci } 206e41f4b71Sopenharmony_ci 207e41f4b71Sopenharmony_ci build() { 208e41f4b71Sopenharmony_ci Column({space: 10}) { 209e41f4b71Sopenharmony_ci Text(this.text).fontSize(50) 210e41f4b71Sopenharmony_ci } 211e41f4b71Sopenharmony_ci .width('100%') 212e41f4b71Sopenharmony_ci .height('100%') 213e41f4b71Sopenharmony_ci .padding(10) 214e41f4b71Sopenharmony_ci } 215e41f4b71Sopenharmony_ci 216e41f4b71Sopenharmony_ci computeTask() { 217e41f4b71Sopenharmony_ci this.count = 0; 218e41f4b71Sopenharmony_ci while (this.count < 10000000) { 219e41f4b71Sopenharmony_ci this.count++; 220e41f4b71Sopenharmony_ci } 221e41f4b71Sopenharmony_ci } 222e41f4b71Sopenharmony_ci} 223e41f4b71Sopenharmony_ci``` 224e41f4b71Sopenharmony_ci 225e41f4b71Sopenharmony_ci#### Recording Data 226e41f4b71Sopenharmony_ci 227e41f4b71Sopenharmony_ciTo record data with AppStartup, perform the following steps: 228e41f4b71Sopenharmony_ci 229e41f4b71Sopenharmony_ci1. Switch to the **Flags** page and set **AppStartup** to **Enabled**. 230e41f4b71Sopenharmony_ci 231e41f4b71Sopenharmony_ci **Figure 10** Enabling AppStartup 232e41f4b71Sopenharmony_ci 233e41f4b71Sopenharmony_ci  234e41f4b71Sopenharmony_ci 235e41f4b71Sopenharmony_ci2. Switch to the **Record template** page, click **Trace template**, and enable **AppStartup**. 236e41f4b71Sopenharmony_ci 237e41f4b71Sopenharmony_ci **Figure 11** Enabling the AppStartup template 238e41f4b71Sopenharmony_ci 239e41f4b71Sopenharmony_ci  240e41f4b71Sopenharmony_ci 241e41f4b71Sopenharmony_ci3. On the **Record setting** tab, customize the recording settings. 242e41f4b71Sopenharmony_ci 243e41f4b71Sopenharmony_ci **Figure 12** Recording settings 244e41f4b71Sopenharmony_ci 245e41f4b71Sopenharmony_ci  246e41f4b71Sopenharmony_ci 247e41f4b71Sopenharmony_ci4. Click **Record** in the upper right corner to start recording. At the same time, open the target application on the device. To end the recording, click **StopRecord**. Alternatively, wait until the recording is complete automatically. When the recording is complete, the page automatically loads the trace data. 248e41f4b71Sopenharmony_ci 249e41f4b71Sopenharmony_ci **Figure 13** Ending recording 250e41f4b71Sopenharmony_ci 251e41f4b71Sopenharmony_ci  252e41f4b71Sopenharmony_ci 253e41f4b71Sopenharmony_ci#### Analyzing Data 254e41f4b71Sopenharmony_ci 255e41f4b71Sopenharmony_ciWait until the analysis result is automatically generated. Click the filter button in the upper right corner and select **AppStartup** to view and analyze data. 256e41f4b71Sopenharmony_ci 257e41f4b71Sopenharmony_ci**Figure 14** Filtering template data 258e41f4b71Sopenharmony_ci 259e41f4b71Sopenharmony_ci 260e41f4b71Sopenharmony_ci 261e41f4b71Sopenharmony_ciExpand the lane of the corresponding application and locate the time frame in which the application is started. Select all phases of the AppStartup lane. You can view the time consumption of each phase in the lower pane. 262e41f4b71Sopenharmony_ci 263e41f4b71Sopenharmony_ci**Figure 15** Time required for each AppStartup phase (before optimization) 264e41f4b71Sopenharmony_ci 265e41f4b71Sopenharmony_ci 266e41f4b71Sopenharmony_ci 267e41f4b71Sopenharmony_ci- **ProcessTouchEvent**: input and processing of click events 268e41f4b71Sopenharmony_ci 269e41f4b71Sopenharmony_ci- **StartUIAbilityBySCB**: process information and window creation 270e41f4b71Sopenharmony_ci 271e41f4b71Sopenharmony_ci- **LoadAbility**: process startup 272e41f4b71Sopenharmony_ci 273e41f4b71Sopenharmony_ci- **Application Launching**: application loading 274e41f4b71Sopenharmony_ci 275e41f4b71Sopenharmony_ci- **UI Ability Launching**: UI ability loading 276e41f4b71Sopenharmony_ci 277e41f4b71Sopenharmony_ci- **UI Ability OnForeground**: application being switched to the foreground. 278e41f4b71Sopenharmony_ci 279e41f4b71Sopenharmony_ci- **First Frame - App Phase**: submission of the first frame for rendering – application 280e41f4b71Sopenharmony_ci 281e41f4b71Sopenharmony_ci- **First Frame - Render Phase**: submission of the first frame for rendering – Render Service 282e41f4b71Sopenharmony_ci 283e41f4b71Sopenharmony_ciAs shown in the preceding figure, the **UI Ability OnForeground** phase takes the longest time, which is 323 ms. 284e41f4b71Sopenharmony_ci 285e41f4b71Sopenharmony_ci**Figure 16** Time required for the **UI Ability OnForeground** phase (before optimization) 286e41f4b71Sopenharmony_ci 287e41f4b71Sopenharmony_ci 288e41f4b71Sopenharmony_ci 289e41f4b71Sopenharmony_ciA closer look at the phase data reveals that the **aboutToAppear** lifecycle callback takes a long time, which is 268 ms, accounting for 82% of the time consumed by the entire **UI Ability OnForeground** phase. 290e41f4b71Sopenharmony_ci 291e41f4b71Sopenharmony_ci**Figure 17** Time required for **aboutToAppear** (before optimization) 292e41f4b71Sopenharmony_ci 293e41f4b71Sopenharmony_ci 294e41f4b71Sopenharmony_ci 295e41f4b71Sopenharmony_ciIt is found in the code that a time-consuming calculation task is executed in the **aboutToAppear** lifecycle callback. This task slows down the cold start of the application. 296e41f4b71Sopenharmony_ci 297e41f4b71Sopenharmony_ciTo speed up application startup, we can conduct asynchronous processing for **aboutToAppear**. The code after optimization is as follows: 298e41f4b71Sopenharmony_ci 299e41f4b71Sopenharmony_ci``` 300e41f4b71Sopenharmony_ci@Entry 301e41f4b71Sopenharmony_ci@Component 302e41f4b71Sopenharmony_cistruct Index { 303e41f4b71Sopenharmony_ci @State private text: string = "hello world"; 304e41f4b71Sopenharmony_ci private count: number = 0; 305e41f4b71Sopenharmony_ci 306e41f4b71Sopenharmony_ci aboutToAppear() { 307e41f4b71Sopenharmony_ci setTimeout(() => { 308e41f4b71Sopenharmony_ci this.computeTask(); 309e41f4b71Sopenharmony_ci }, 0) 310e41f4b71Sopenharmony_ci } 311e41f4b71Sopenharmony_ci 312e41f4b71Sopenharmony_ci build() { 313e41f4b71Sopenharmony_ci Column({space: 10}) { 314e41f4b71Sopenharmony_ci Text(this.text).fontSize(10) 315e41f4b71Sopenharmony_ci } 316e41f4b71Sopenharmony_ci .width('100%') 317e41f4b71Sopenharmony_ci .height('100%') 318e41f4b71Sopenharmony_ci .padding(10) 319e41f4b71Sopenharmony_ci } 320e41f4b71Sopenharmony_ci 321e41f4b71Sopenharmony_ci computeTask() { 322e41f4b71Sopenharmony_ci this.count = 0; 323e41f4b71Sopenharmony_ci while (this.count < 10000000) { 324e41f4b71Sopenharmony_ci this.count++; 325e41f4b71Sopenharmony_ci } 326e41f4b71Sopenharmony_ci } 327e41f4b71Sopenharmony_ci} 328e41f4b71Sopenharmony_ci``` 329e41f4b71Sopenharmony_ci 330e41f4b71Sopenharmony_ciNow, let's record the data in the same way. 331e41f4b71Sopenharmony_ci 332e41f4b71Sopenharmony_ci**Figure 18** Time required for each AppStartup phase (after optimization) 333e41f4b71Sopenharmony_ci 334e41f4b71Sopenharmony_ci 335e41f4b71Sopenharmony_ci 336e41f4b71Sopenharmony_ciThe focus of optimization, the **UI Ability OnForeground** phase, where the **aboutToAppear** lifecycle is located, takes 81 ms. 337e41f4b71Sopenharmony_ci 338e41f4b71Sopenharmony_ci**Figure 19** Time required for the UI Ability OnForeground phase (after optimization) 339e41f4b71Sopenharmony_ci 340e41f4b71Sopenharmony_ci 341e41f4b71Sopenharmony_ci 342e41f4b71Sopenharmony_ciA closer look at the phase data reveals that the **aboutToAppear** lifecycle callback now takes 2 ms, accounting for only 2.5% of the time consumed by the entire **UI Ability OnForeground** phase. 343e41f4b71Sopenharmony_ci 344e41f4b71Sopenharmony_ci**Figure 20** Time consumed by aboutToAppear (after optimization) 345e41f4b71Sopenharmony_ci 346e41f4b71Sopenharmony_ci 347