アプリケーション開発において、高性能プログラミングはユーザーエクスペリエンスの向上に不可欠です。本記事では、鸿蒙Next ArkTSにおける高性能プログラミングの手法について詳しく解説します。変数宣言と式、関数、配列、例外処理などの最適化テクニックを紹介し、効率的なアプリケーションの開発を支援します。
変数宣言と式
変更されない変数にはconstを使用する
コード内で後から値が変わらない変数は、constで宣言することをお勧めします。これにより、コードの意図が明確になり、コンパイラによる最適化も促進されます。
const index = 10000; // この変数は後続のコードで変更されないため、定数として宣言することが望ましい
number型変数の整数と浮動小数点数の混用を避ける
ArkTSランタイムは、整数と浮動小数点数を区別して最適化を行います。そのため、変数の初期化後はデータ型を変更しないようにします。
let intNum = 1;
intNum = 1.1; // この変数は初期化時に整数として宣言されているため、その後に浮動小数点数を代入しないことを推奨します
let doubleNum = 1.1;
doubleNum = 1; // この変数は初期化時に浮動小数点数として宣言されているため、その後に整数を代入しないことを推奨します
数値計算でのオーバーフローアバウトを避ける
数値計算を行う際には、オーバーフローを引き起こす可能性のあるシナリオに注意してください。加算、減算、乗算、指数演算、ビットAND(&)、無符号右シフト(>>)などの演算が該当します。オーバーフローはエンジンが遅いオーバーフローロジックブランチに入り込むため、パフォーマンスに影響を与えます。したがって、数値が適切な範囲内であることを確認し、INT32_MAXを超えるかINT32_MIN未満になることを避けてください。
ループ内の定数抽出
ループ内でアクセスされる定数がある場合、その定数がループ中に変更されない場合は、ループの外側に抽出します。これにより、プロパティへのアクセス回数が削減され、パフォーマンスが向上します。
class Time {
static start: number = 0;
static info: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
}
function getNum(num: number): number {
let total: number = 348;
for (let index: number = 0x8000; index > 0x8; index >>= 1) {
total += ((Time.info[num - Time.start] & index) !== 0) ? 1 : 0;
}
return total;
}
// 最適化後のコード
function getNum(num: number): number {
let total: number = 348;
const info = Time.info[num - Time.start]; // ループから不変量を抽出
for (let index: number = 0x8000; index > 0x8; index >>= 1) {
if ((info & index) !== 0) {
total++;
}
}
return total;
}
関数
関数の外部変数をパラメータで渡すことを推奨する
クロージャーを使用すると、追加のクロージャー作成とアクセスコストが生じます。パフォーマンスに敏感な場面では、クロージャーを使用せず、関数の外部変数をパラメータで渡すことをお勧めします。
// クロージャーを使用しない方が望ましい
let arr = [0, 1, 2];
function foo(): number {
return arr[0] + arr[1];
}
foo();
// パラメータを使用する方法を推奨
let arr = [0, 1, 2];
function foo(array: number[]): number {
return array[0] + array[1];
}
foo(arr);
オプショナルパラメータの使用を避ける
オプショナルパラメータは、パラメータがundefinedである可能性があります。そのため、関数内でそのパラメータを使用する際には、非nullチェックが必要となり、追加のコストが発生します。ビジネスロジックに応じて、パラメータを必須にするか、デフォルトパラメータを使用することを検討してください。
// オプショナルパラメータを使用しない方が望ましい
function add(left?: number, right?: number): number | undefined {
if (left !== undefined && right !== undefined) {
return left + right;
}
return undefined;
}
// デフォルトパラメータを使用する方法を推奨
function add(left: number = 0, right: number = 0): number {
return left + right;
}
配列
数値配列にはTypedArrayを使用する
純粋な数値計算を行う場合、TypedArrayを使用することをお勧めします。これにより、より良いパフォーマンスを得ることができます。
// 最適化前
const arr1 = new Array<number>([1, 2, 3]);
const arr2 = new Array<number>([4, 5, 6]);
let res = new Array<number>(3);
for (let i = 0; i < 3; i++) {
res[i] = arr1[i] + arr2[i];
}
// 最適化後
const typedArray1 = new Int8Array([1, 2, 3]);
const typedArray2 = new Int8Array([4, 5, 6]);
let res = new Int8Array(3);
for (let i = 0; i < 3; i++) {
res[i] = typedArray1[i] + typedArray2[i];
}
疎配列を使用しない
ランタイムが1024以上のサイズの配列を割り当てたり、疎配列を扱ったりすると、ハッシュテーブルを使用して要素を格納するため、オフセットアクセスよりも遅くなります。コード開発では、配列が疎になることを避けることが重要です。
// 100000の大きさの配列を直接割り当てると、ランタイムがハッシュテーブルを使用して要素を格納します
let count = 100000;
let result: number[] = new Array(count);
// 配列を作成後、9999番目の要素に値を代入すると疎配列になります
let result: number[] = new Array();
result[9999] = 0;
ユニオンタイプの配列を使用しない
数値配列に整数と浮動小数点数を混在させたり、ユニオンタイプの配列を使用したりすることは避けてください。ビジネスロジックに応じて、同じタイプのデータを同じ配列内に配置します。
// 推奨しない例
let arrNum: number[] = [1, 1.1, 2]; // 数値配列に整数と浮動小数点数を混在させています
let arrUnion: (number | string)[] = [1, 'hello']; // ユニオンタイプの配列
// 推奨される例
let arrInt: number[] = [1, 2, 3];
let arrDouble: number[] = [0.1, 0.2, 0.3];
let arrString: string[] = ['hello', 'world'];
例外
例外を頻繁に投げない
例外を作成する際には、スタックフレームが構築され、パフォーマンスに影響を与えます。パフォーマンスに敏感な場面(例えばforループ内)では、例外を頻繁に投げないようにします。
// 最適化前
function div(a: number, b: number): number {
if (a <= 0 || b <= 0) {
throw new Error('Invalid numbers.');
}
return a / b;
}
function sum(num: number): number {
let sum = 0;
try {
for (let t = 1; t < 100; t++) {
sum += div(t, num);
}
} catch (e) {
console.log(e.message);
}
return sum;
}
// 最適化後
function div(a: number, b: number): number {
if (a <= 0 || b <= 0) {
return NaN;
}
return a / b;
}
function sum(num: number): number {
let sum = 0;
for (let t = 1; t < 100; t++) {
if (t <= 0 || num <= 0) {
console.log('Invalid numbers.');
}
sum += div(t, num);
}
return sum;
}