• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ArkTS Performant Programming Practices
2
3<!--Kit: ArkTS-->
4<!--Subsystem: ArkCompiler-->
5<!--Owner: @zhanyi819-->
6<!--Designer: @qyhuo32-->
7<!--Tester: @kirl75; @zsw_zhushiwei-->
8<!--Adviser: @zhang_yixin13-->
9
10## Overview
11
12This topic provides a set of performant programming practices that you can apply in performance-critical scenarios. They are techniques and recommendations drawn from real-world development. Following these practices in your service implementation can help develop performant applications. For details about ArkTS programming specifications, see [ArkTS Coding Style Guide](./arkts-coding-style-guide.md).
13
14## Declarations and Expressions
15
16### Using const to Declare Unchanged Variables
17
18You are advised to use **const** to declare variables that remain unchanged.
19
20``` TypeScript
21const index = 10000; // This variable does not change in the subsequent process. You are advised to declare it as a constant.
22```
23
24
25### Avoiding Mixed Use of Integers and Floating-Point Numbers in Variables of the number Type
26
27For variables of the **number** type, integer data and floating-point data are distinguished during optimization at runtime. As such, avoid changing the data type of the variables after they have been initialized.
28
29``` TypeScript
30let intNum = 1;
31intNum = 1.1; // This variable is declared as an integer data type. Avoid assigning a floating-point number to it.
32
33let doubleNum = 1.1;
34doubleNum = 1; // This variable is declared as a floating-point data type. Avoid assigning an integer to it.
35```
36
37
38### Avoiding Overflow in Arithmetic Operations
39
40When arithmetic operations run into overflow, the engine enters a slower logic branch for processing overflow, affecting subsequent performance. Below are some suggestions to mitigate the overflow risk.
41
42- For operations such as addition, subtraction, multiplication, and exponentiation, the value should not be greater than **INT32_MAX** (2147483647) or less than **INT32_MIN** (-2147483648).
43
44- For operations such as & (and) and >>> (unsigned right shift), the value should not be greater than **INT32_MAX**.
45
46
47### Extracting Constants in Loops to Reduce Attribute Access Times
48
49If the constants do not change in the loop, they can be extracted outside the loop to reduce the number of access times.
50
51``` TypeScript
52class Time {
53  static start: number = 0;
54  static info: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
55}
56
57function getNum(num: number): number {
58  let total: number = 348;
59  for (let index: number = 0x8000; index > 0x8; index >>= 1) {
60    // The system searches for info and start of Time multiple times, and the values found each time are the same.
61    total += ((Time.info[num - Time.start] & index) !== 0) ? 1 : 0;
62  }
63  return total;
64}
65```
66
67You can extract **Time.info[num - Time.start]** as a constant to reduce the number of attribute access times and improves performance. The optimized code is as follows.
68
69``` TypeScript
70class Time {
71  static start: number = 0;
72  static info: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
73}
74
75function getNum(num: number): number {
76  let total: number = 348;
77  const info = Time.info[num - Time.start];  // Extract constants from the loop.
78  for (let index: number = 0x8000; index > 0x8; index >>= 1) {
79    if ((info & index) != 0) {
80      total++;
81    }
82  }
83  return total;
84}
85```
86
87
88## Functions
89
90### Using Parameters to Pass External Variables
91
92Using closures may cause extra overhead.
93
94``` TypeScript
95let arr = [0, 1, 2];
96
97function foo(): number {
98  return arr[0] + arr[1];
99}
100
101foo();
102```
103
104In performance-sensitive scenarios, you are advised to pass external variables using parameters.
105``` TypeScript
106let arr = [0, 1, 2];
107
108function foo(array: number[]): number {
109  return array[0] + array[1];
110}
111
112foo(arr);
113```
114
115
116### Avoiding Optional Parameters
117
118An optional function parameter may be **undefined**. When such a parameter is used in the function, the system needs to check whether the parameter is null, which will cause extra overhead.
119
120``` TypeScript
121function add(left?: number, right?: number): number | undefined {
122  if (left != undefined && right != undefined) {
123    return left + right;
124  }
125  return undefined;
126}
127```
128
129Declare function parameters as mandatory parameters based on service requirements. You can use the default parameters.
130``` TypeScript
131function add(left: number = 0, right: number = 0): number {
132  return left + right;
133}
134```
135
136
137## Arrays
138
139### Prioritizing TypedArray for Value Arrays
140
141Where only arithmetic operations are involved, use **TypedArray**.
142
143Sample code before optimization:
144``` TypeScript
145const arr1 = new Array<number>(1, 2, 3);
146const arr2 = new Array<number>(4, 5, 6);
147let res = new Array<number>(3);
148for (let i = 0; i < 3; i++) {
149  res[i] = arr1[i] + arr2[i];
150}
151```
152
153Sample code after optimization:
154``` TypeScript
155const typedArray1 = Int8Array.from([1, 2, 3]);
156const typedArray2 = Int8Array.from([4, 5, 6]);
157let res = new Int8Array(3);
158for (let i = 0; i < 3; i++) {
159  res[i] = typedArray1[i] + typedArray2[i];
160}
161```
162
163
164### Avoiding Sparse Arrays
165
166When allocating an array whose size exceeds 1024 bytes or a sparse array during runtime, a hash table is used to store elements. This mode results in slower access speeds. Therefore, you should avoid converting arrays to sparse arrays during code development.
167
168``` TypeScript
169// Allocate an array of 100,000 bytes, for which a hash table is used to store elements.
170let count = 100000;
171let result: number[] = new Array(count);
172
173// The array will become a sparse array when the value is changed to 9999 after the array is created.
174let result: number[] = new Array();
175result[9999] = 0;
176```
177
178
179### Avoiding Arrays of Union Types
180
181When appropriate, use arrays that contain elements of the same type. That is, avoid using arrays of union types. Avoid mixed use of integer data and floating-point data in number arrays.
182
183``` TypeScript
184let arrNum: number[] = [1, 1.1, 2];  // Both integer data and floating-point data are used in a value array.
185
186let arrUnion: (number | string)[] = [1, 'hello'];  // Union array.
187```
188
189Place the data of the same type in the same array based on service requirements.
190``` TypeScript
191let arrInt: number[] = [1, 2, 3];
192let arrDouble: number[] = [0.1, 0.2, 0.3];
193let arrString: string[] = ['hello', 'world'];
194```
195
196
197## Exceptions
198
199### Avoiding Frequent Exceptions
200
201Creating exceptions involves constructing the stack frame for the exception, which may performance overhead. In light of this, avoid frequently throwing exceptions in performance-sensitive scenarios, for example, in **for** loop statements.
202
203Sample code before optimization:
204
205``` TypeScript
206function div(a: number, b: number): number {
207  if (a <= 0 || b <= 0) {
208    throw new Error('Invalid numbers.')
209  }
210  return a / b
211}
212
213function sum(num: number): number {
214  let sum = 0
215  try {
216    for (let t = 1; t < 100; t++) {
217      sum += div(t, num)
218    }
219  } catch (e) {
220    console.log(e.message)
221  }
222  return sum
223}
224```
225
226Sample code after optimization:
227
228``` TypeScript
229function div(a: number, b: number): number {
230  if (a <= 0 || b <= 0) {
231    return NaN
232  }
233  return a / b
234}
235
236function sum(num: number): number {
237  let sum = 0
238  for (let t = 1; t < 100; t++) {
239    if (t <= 0 || num <= 0) {
240      console.log('Invalid numbers.')
241    }
242    sum += div(t, num)
243  }
244  return sum
245}
246```
247