ホームページ  >  記事  >  ウェブフロントエンド  >  高パフォーマンスの JavaScript を作成する

高パフォーマンスの JavaScript を作成する

黄舟
黄舟オリジナル
2017-02-21 11:45:441531ブラウズ



高パフォーマンスの JavaScript を作成する

この記事の本来の目的は、いくつかの簡単なコーディングのヒントを使用して JavaScript コンパイラーの最適化プロセスを促進し、コードの効率を向上させる方法を紹介することです。特にガベージコレクションの速度が速いゲームでは、パフォーマンスが少し悪いとユーザーに白い画面が表示されます。

単相性: 単相性

JavaScript では、関数を呼び出すときに動的パラメーターを渡すことができますが、単純な 2 パラメーター関数を例にとると、コンパイラーはパラメーターの型、パラメーターの数、戻り値の型が呼び出されるタイミングのみを決定できます。動的に解析すると時間がかかります。コンパイラーは当然、単相的に予測可能なデータ構造、パラメーター統計などを処理できるようにしたいと考えています。

function example(a, b) {
  // we expect a, b to be numeric
  console.log(++a * ++b);
};

example(); // bad
example(1); // still bad
example("1", 2); // dammit meg

example(1, 2); // good

定数: 定数

定数を使用すると、コンパイラはコンパイル中に変数の値の置換を完了できます。

const a = 42; // we can easily unfold this
const b = 1337 * 2; // we can resolve this expression
const c = a + b; // still can be resolved
const d = Math.random() * c; // we can only unfold 'c'

// before unfolding
a;
b;
c;
d;

// after unfolding
// we can do this at compile time!
42;
2674;
2716;
Math.random() * 2716;

インライン化: インライン化

JIT コンパイラは、最も多く実行されたコードの部分を見つけることができます。コードをより小さなチャンクに分割すると、コンパイラーがコンパイル時にこれらのチャンクをインライン形式に変換し、実行速度を向上させるのに役立ちます。

データ型: データ型

数値とブール値は、文字列などの他のプリミティブ型よりもパフォーマンスが優れているため、できるだけ使用してください。文字列型を使用すると、追加のガベージ コレクション コストが発生する可能性があります。

const ROBOT = 0;
const HUMAN = 1;
const SPIDER = 2;

let E_TYPE = {
  Robot: ROBOT,
  Human: HUMAN,
  Spider: SPIDER
};

// bad
// avoid uncached strings in heavy tasks (or better in general)
if (entity.type === "Robot") {
  
}

// good
// the compiler can resolve member expressions
// without much deepness pretty fast
if (entity.type === E_TYPE.Robot) {
  
}

// perfect
// right side of binary expression can even get unfold
if (entity.type === ROBOT) {
  
}

厳密演算子と抽象演算子

可能な限り == 演算子の代わりに === 厳密比較演算子を使用してください。厳密な比較演算子を使用すると、コンパイラーによる型推定と変換の実行を回避できるため、特定のパフォーマンスが向上します。

厳密な条件

JavaScript の if ステートメントも非常に柔軟で、同様の値を if(a) then bla タイプの条件選択ステートメントに直接渡すことができます。ただし、この場合も前述の厳密比較演算子や緩比較演算子と同様に、コンパイラで複数のデータ型に変換して比較する必要があり、すぐに結果を得ることができません。もちろん、これは略語の使用に対する盲目的な反対ではありませんが、パフォーマンスを重視するシナリオでは、あらゆる詳細を最適化することをお勧めします:

let a = 2;

// bad
// abstracts to check in the worst case:
// - is value equal to true
// - is value greater than zero
// - is value not null
// - is value not NaN
// ..
if (a) {
 // if a is true, do something 
}

// good
if (a === 2) {
  // do sth 
}

// same goes for functions
function b() {
  return (!false);
};

if (b()) {
  // get in here slow
}

if (b() === true) {
  // get in here fast
  // the compiler knows a specific value to compare with
}

Arguments

パラメーターを取得するために argument[index] メソッドを使用することは避けてください。そして、渡されたパラメータ変数の変更はできるだけ避けるようにしてください:

function mul(a, b) {
  return (arguments[0]*arguments[1]); // bad, very slow
  return (a*b); // good
};

function test(a, b) {
  a = 5; // bad, dont modify argument identifiers
  let tmp = a; // good
  tmp *= 2; // we can now modify our fake 'a'
};

Toxicity: これらのキーワードは有毒です

Toxicity

以下にリストされているいくつかの文法的特徴は、最適化プロセスに影響します:

  • eval

  • with

  • try/catch

同時に、関数や関数内でのクロージャの宣言は避けてください。これにより、多数の操作でガベージ コレクション操作が過剰に発生する可能性があります。

Objecs

オブジェクト インスタンスは通常、暗黙的なクラスを共有するため、インスタンスの未定義変数にアクセスしたりその値を設定したりすると、暗黙的なクラスが作成されます。

// our hidden class 'hc_0'
class Vector {
  constructor(x, y) {
    // compiler finds and expects member declarations here
    this.x = x;
    this.y = y;
  }
};

// both vector objects share hidden class 'hc_0'
let vec1 = new Vector(0, 0);
let vec2 = new Vector(2, 2);

// bad, vec2 got hidden class 'hc_1' now
vec2.z = 0;

// good, compiler knows this member
vec2.x = 1;

Loops

配列の長さの計算値を可能な限りキャッシュし、単一の型を可能な限り同じ配列に格納します。 for-in 構文を使用して配列を反復処理すると非常に時間がかかるため、使用しないでください。また、ループ内の continue ステートメントや Break ステートメントのパフォーマンスも良好なので、使用する際に心配する必要はありません。さらに、短い論理部分をできる限り独立した関数に分割すると、コンパイラの最適化がより容易になります。さらに、プレフィックス自動インクリメント式を使用すると、パフォーマンスが若干向上する可能性があります。 (i++ ではなく ++i)

let badarray = [1, true, 0]; // bad, dont mix types
let array = [1, 0, 1]; // happy compiler

// bad choice
for (let key in array) {
  
};

// better
// but always try to cache the array size
let i = 0;
for (; i < array.length; ++i) {
  key = array[i];
};

// good
let i = 0;
let key = null;
let length = array.length;
for (; i < length; ++i) {
  key = array[i];
};

drawImage

draeImage 関数は、最も高速な 2D Canvas API の 1 つですが、描画の便宜のためにすべてのパラメーターを省略すると、パフォーマンスの低下も増大することに注意する必要があります。

// bad
ctx.drawImage(
  img,
  x, y
);

// good
ctx.drawImage(
  img,
  // clipping
  sx, sy,
  sw, sh,
  // actual stuff
  x, y,
  w, h
);

// much hax
// no subpixel rendering by passing integers
ctx.drawImage(
  img,
  sx|0, sy|0,
  sw|0, sh|0,
  x|0, y|0,
  w|0, h|0
);

上記は、高性能 JavaScript の記述内容です。その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) をご覧ください。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。