1# JSVM Tuning Practices
2
3## JSVM Call Invocation
4
5The process of executing JavaScript (JS) code on a JavaScript virtual machine (JSVM) can be divided into the following layers:
6
7- Native: logic layer for the application to run JS code. In this layer, the APIs provided by the JSVM are used to compile and run JS code and create a code cache.
8- JSVM-API: intermediate layer between C/C++ and V8 JS engine. This layer ensures compatibility with JS engines of different versions and provides standardized practices of JS engines.
9- JSVM: JS engine layer, in which JS code is compiled and executed.
10
11If the application startup is slowed down due to unnecessary overheads generated by using of the JSVM, you can analyze the cause and make optimization in the three layers.
12
13## Accelerating Startup
14
15For the applications that use JSVM, optimization can be made in the cold startup and hot startup.
16In the cold startup, generally, the initial startup, there is no profile or cache that can be used for optimization.
17In the hot startup, the code cache can be used for optimization.
18
19### Reducing Overheads of the JSVM Layer
20
21Some of the overheads in the JSVM layer are generated from compilation. You can adjust the options passed in when JSVM-API is called to reduce the compilation overhead of the JS engine in the main thread.
22
23In the following example, the **eagerCompile** parameter specifies the compilation behavior. You can enable this option in the startup scenarios to optimize the compilation effect.
24
25```cpp
26/**
27 * ...
28 * @param eagerCompile: Whether to compile the script eagerly.
29 * ...
30 */
31JSVM_EXTERN JSVM_Status OH_JSVM_CompileScript(JSVM_Env env,
32                                              JSVM_Value script,
33                                              const uint8_t* cachedData,
34                                              size_t cacheDataLength,
35                                              bool eagerCompile, // Set it to true to enable full compilation.
36                                              bool* cacheRejected,
37                                              JSVM_Script* result);
38```
39
40Using a code cache also helps speed up compilation. For details, see [Accelerating Compilation Using a Code Cache](use-jsvm-about-code-cache.md).
41
42#### Hot Startup: Creating a Code Cache with Adequate Code
43
44To speed up hot startup, create a code cache with adequate code to reduce the compilation overhead. The code coverage of the created code cache determines the optimization effect of the hot startup.
45
46To create a code cache with adequate code, set **eagerCompile** to **true** for the compilation before the code cache is created. In this way, full compilation is performed in V8, which ensures high code coverage of the cache.
47
48This approach, however, causes extra compilation time, which may increase the cold startup time used. Read on to learn the ways to optimize the cold startup in the native layer.
49
50#### Cold Startup: Disabling eagerCompile
51
52Enabling **eagerCompile** increases the compilation time for cold startup. To reduce the compilation time, you can use V8 lazy compile, which delays the compilation of JS code until it is executed.
53
54By disabling **eagerCompile** for the code that may block the main thread, you can speed up the code startup.
55
56### Reducing Time Overheads in the Native Layer
57#### Reducing the Code Cache Impact in Cold Startup
58
59It seems contradictory since you are advised to enable **eagerCompile** for higher hot startup performance and to disable **eagerCompile** for higher cold startup performance. To avoid the trade-off between cold startup and hot startup performance, let's focus on the code cache itself.
60
61Code compilation is required before a code cache is created, and the creation of the code cache generates overheads.
62
63To use a code cache without compromising the cold startup speed in the native layer, you can start another thread to create the code cache.
64
65You can use either of the following methods to achieve this purpose:
66
67- Start another thread to complete the code compilation and create a code cache. In this way, you can enable **eagerCompile** for code cache creation and disable it for cold startup. This approach decouples the code cache creation and the application running, and you do not need to consider the time when the code cache is created. However, the peak resource usage during the application running may increase.
68
69  The pseudo-code of this process is as follows:
70
71  ```
72  async_create_code_cache() {
73    compile_with_eager_compile();
74    create_code_cache();
75    save_code_cache();
76  }
77
78  ...
79
80  if (has_code_cache) {
81    evaluate_script_with_code_cache();
82  } else {
83    start_thread(async_create_code_cache());
84    evaluate_script_without_code_cache();
85  }
86  ```
87
88- After all the paths are executed in the startup, start a new thread to create a code cache. In this way, you can create a cache with sufficient code without enabling **eagerCompile** while maintaining the hot startup performance. This approach will not increase the peak resource usage or affect the I/O. However, the time for generating the code cache is restricted.
89
90  The pseudo-code of this process is as follows:
91
92  ```
93  async_create_code_cache() {
94    compile_with_out_eager_compile();
95    create_code_cache();
96    save_code_cache();
97  }
98
99  ...
100
101  if (has_code_cache) {
102    evaluate_script_with_code_cache();
103  } else {
104    evaluate_script_without_code_cache();
105  }
106
107  ...
108
109  if (script_run_completed) {
110    start_thread(async_create_code_cache());
111  }
112  ```
113
114
115### Using Performant JSVM-API
116
117You can use more performant APIs provided by JSVM-API to improve performance.
118
119#### Using IsXXX Instead of TypeOf
120
121To determine the native type of an object, it is inefficient to use **OH_JSVM_TypeOf** to obtain the object type and check whether the object type is the same as a specific type. Instead, you can use the **Is**XXX APIs.
122
123- Example (not recommended):
124
125
126  ```cpp
127  bool Test::IsFunction() const {
128      HandleScopeInit(*env);
129      JSVM_Value jsvmValue;
130      ObjectWrappingGet(*env, jsvmRef, jsvmValue);
131      // Type judgment starts
132      bool result;
133      JSVM_ValueType valueType;
134      OH_JSVM_TypeOf(*env, jsvmValue, &valueType);
135      OH_JSVM_CloseHandleScope(*env, scope);
136      result = (valueType == JSVM_FUNCTION)
137      // Type judgment ends.
138      return result;
139  }
140  ```
141
142- Example (recommended):
143
144
145  ```cpp
146  bool Test::IsFunction() const {
147      HandleScopeInit(*env);
148      JSVM_Value jsvmValue;
149      ObjectWrappingGet(*env, jsvmRef, jsvmValue);
150      // Type judgment starts.
151      bool result;
152      OH_JSVM_IsFunction(*env, jsvmValue, &result); // Check whether the object type is function.
153      OH_JSVM_CloseHandleScope(*env, scope);
154      // Type judgment ends.
155      return result;
156  }
157  ```
158
159
160
161The following optimization decreases the code execution time by 150 ms, accounting for about 5% of the performance benefits of an applet.
162
163#### Using OH_JSVM_CreateReference to Avoid Creating Redundant Objects
164
165Generally, the procedure for creating a reference to an object is as follows:
166
167Create an object -> Set a value of the object -> Create the reference to the object
168
169If the object already has the value, you can directly create a reference to the value.
170
171- Example (not recommended):
172
173  ```cpp
174  // Open a handle scope.
175  JSVM_HandleScope scope;
176  OH_JSVM_OpenHandleScope(*env, &scope);
177  // Obtain JSVM_Value.
178  JSVM_Value jsvmValue;
179  OH_JSVM_GetNull(*env, &jsvmValue);
180  // Create and store reference for JSVM_Value.
181  JSVM_Value wrappingObject;
182  OH_JSVM_CreateObject(*env, &wrappingObject);
183  OH_JSVM_SetElement(*env, wrappingObject, 1, jsvmValue);
184  OH_JSVM_CreateReference(*env, wrappingObject, 1, &result->p_member->jsvmRef);
185  // Close the handle scope.
186  OH_JSVM_CloseHandleScope(*env, scope);
187  ```
188
189- Example (recommended):
190
191  ```cpp
192  // Open a handle scope.
193  JSVM_HandleScope scope;
194  OH_JSVM_OpenHandleScope(*env, &scope);
195  // Obtain JSVM_Value.
196  JSVM_Value jsvmValue;
197  OH_JSVM_GetNull(*env, &jsvmValue);
198  // Create and store reference for JSVM_Value.
199  OH_JSVM_CreateReference(*env, jsvmValue, 1, &result->p_member->jsvmRef); // Create a reference to an object of any type, making your code simpler and more performant.
200  // Close the handle scope.
201  OH_JSVM_CloseHandleScope(*env, scope);
202  ```
203
204This optimization also helps reduce the number of API calls for another applet and decreases the code execution time by 100+ ms, accounting for about 3% of the performance benefits.
205