2010年10月31日日曜日

JavaScriptでよくある性能劣化ポイント

WEB+DB PRESS vol.59より。
自分のためのメモ代わりとして、気になった部分をピックアップ。

まずポイント列挙
JavaScriptコード上でよく見られる性能劣化ポイント(抜粋)。
以下、まず列挙します。リンクはページ内アンカです。
  1. 意図しない変数のグローバル化
  2. コストの高い配列アクセス方法を利用
  3. try/catchの間違った使用
  4. ブラウザの再描画について

意図しない変数のグローバル化
JavaScriptでは、関数内で変数を参照する際にまず自身のスコープ内を探します。
見つからない場合、入れ子になっている関数であれば外側の関数のスコープをたどっていき、グローバルスコープは必ず最後に探索されます(スコープチェイン)。そのため、グローバル変数を発見するのは時間がかかることになります。
また、「var」をつけずに宣言された変数はグローバル変数となります。
なので、グローバルにする必要のない変数は必ず「var」を付けて宣言しましょう。
関連:JavaScriptのクロージャ、スコープチェインについての記事(World Wide Windblue)

コストの高い配列アクセス方法を利用
配列にアクセスするには、以下の2通りの方法がある。
  1. メソッド呼び出し
    後発で使用追加されたメソッドを呼び出すこと。(Array.pushなど)
  2. プリミティブ操作
    原始的なアクセス方法

プリミティブ操作を使用するほうがパフォーマンスが高くなる。

try/catchの間違った使用
catch節が実行されるたびに新しいスコープが生成・破棄されるため、頻繁に実行されるとパフォーマンスが低下する可能性があります。
入力チェック時などエラー判定をするときなどでも、別の方法があるならtry/catch節を排除することが望ましいケースが多いでしょう。

2010 11/1 追記ここから
実際に試してみる。
エラー判定にtry/catchを使わない場合と使った場合で比較。
比較したブラウザはIE7、Firefox3.6.8、Google Chrome7、Opera10.63。
各検証を10回して平均を取った。

エラー判定にtry/catchを使わない場合。
window.onload = function(){
  var start = new Date();
  for(var i = 0;i < 10000; i++){
    var a = 100;
    if(typeof a === 'string'){
      return a.substring(0, 2);
    }else{
    }
  }
  var end = new Date();
  alert((end - start));
}

1回の実行にかかった平均の時間。
IE…11(ms)
Firefox…0.6
Google Chrome…0
Opera…1.9

エラー判定にtry/catchを使わない場合。
window.onload = function(){
  var start = new Date();
  var a = 100;
  for(var i = 0;i < 10000; i++){
    try{
      a.substring(0, 2);
    }catch(e){
    }
  }
  var end = new Date();
  alert((end - start));
}

1回の実行にかかった平均の時間。
IE…540.7(ms)
Firefox…760.7
Google Chrome…1395
Opera…28.9

(Operaが早すぎて不安です。PCのスペックが低いのも関係あるかもしれません)
2010 11/1 追記ここまで

また、catch節の実行時に生成されるスコープは、ローカルスコープよりも上位に位置します。
そのため、catch節の中で変数を定義することは、それだけで処理のオーバーヘッドになります。

ダメな例。
try{
  ...
}catch(error){
  if(error.code === '500'){
    var c = error.code;
    ...
  }
}

この倍、errorオブジェクトに対する処理は別の関数に委譲するコトでパフォーマンスへの影響を最小限に抑えられる。
関数かされた処理はcatchスコープから呼び出されても、ローカル変数は通常のローカル変数スコープ内に定義されます。
よい例。
try{
  ...
}catch(error){
  handleError(error);
}


ブラウザの再描画について
JavaScriptによりDOM要素の位置やサイズ変更、新たなDOM要素の追加などを行うと、ブラウザはその変更を表示されているWebページに反映しなくてはなりません。
このような場合にブラウザの再描画が発生しますが、再描画に要するコストは高く、頻繁に発生すると深刻なパフォーマンス低下を招きます。
再描画の回数を減らすポイントは次の2点。
  1. DOMに対する「スタイルの取得」と「スタイルの変更」を混ぜない
  2. DOMに対する「スタイルの変更」は短時間ですばやく行う

Webサイト高速化のルール

WEB+DB PRESS vol.59より。
14個列挙されていたものからいくつかピックアップ。

  1. Expiresヘッダを利用する
    サーバサオdpで付与可能なレスポンスヘッダで、該当するコンポーネントのキャッシュ有効範囲を設定できる。

  2. コンテンツはgzip圧縮する
    テキスト系コンポーネント(HTML/CSS/JavaScript)はgzip圧縮をすることでデータ量を大きく削減できる。
    ただし、ブラウザやバージョンの際によって問題が発生することもある。

  3. CSSは上に配置する
    CSSはheadタグ内にlinkタグで連続して配置しなければいけない。
    「@import」やHTMLの後半で指定するようなケースではレンダリングの遅延を招くケースがある。

  4. JavaScriptは下に配置する
    JavaScriptはほとんどのケースでbodyの閉じタグ直前に配置して問題のないケースが多い。
    HTML丈夫に記述するとダウンロードやレンダリングの中断を招く可能性があり、高速化の観点上、最も大きな遅延を招く可能性もある。

  5. DNS参照を最小限に抑える
    ページをダウンロードする際に、そのドメインの数だけDNSのルックアップ処理が発生するため。
    (CDNを利用するなどとは矛盾する部分もあるが…)

  6. Ajax通信はキャッシュする
    頻繁に発生しないリクエストやAjax通信によりサーバサイドでの更新処理が発生する場合など、キャッシュするべきかどうかを確認する必要がある。

Webサイト高速化に対する分析ツール

WEB+DB PRESS vol.59より。

  1. YSlow
    Yahoo!inc.が開発したWebサイトのパフォーマンス計測ツール→本家のサイト

    Firefoxで動作するアドオン。MozillaのAdd-onsページよりダウンロード可能。
    【ハウツー】YSlowでWebページを高速化 - リッチさと速さを同時に実現するUIを!(マイコミジャーナル)に使い方が載っている。

  2. Firebug
    こちらもFirefoxのAdd-onで動作する。ダウンロードページ→MozillaのAdd-onsページ
    HTMLやCSS、JavaScriptのデバッグやDOM操作を行うための開発ツール。

  3. AOL Pagetest
    Internet Explorer向けにALO Inc.が主体となって開発したオープンソースのツール。
    ダウンロードはこちらから。→SourceForge.net
    Firebugの「接続」タブのInternet Explorerのようなイメージらしい。
    AOL製のIE用Webパフォーマンスツール「Pagetest」(MOONGIFT)に詳しい使い方が紹介されている。

  4. Page Speed
    Googleが開発したFirefoxのadd-on。
    Googleのダウンロードページから入手できる。
    基本的にはYSlowに近い機能を持つよう。
    PageSpeed サイト応答速度調査ツール 使ってみました。 « SE99隊.jpに詳しい使い方が載っている。


Related Posts Plugin for WordPress, Blogger...