document

HTML5 & JavaScript side
twitter: @y_imaya

2012年08月

jsPerf, JSPerfView を使った、JavaScript コードのベンチマーク計測とブログなどで計測結果を利用する方法

jsPerf とは

JavaScript のコードスニペットに対してベンチマークを計測するサービスです。

一般的に、コードの速度を計測する際は console.time, console.timeEnd を使う事が多いと思いますが、 実行するたびに結果がブレたり、短い処理では正確な比較ができなかったりします。

jsPerf では何度か同じ処理を実行して最終的に一秒間に何回実行できたかをスコアにするので、実行時間が 1ms より小さい処理でも計測できたり、ブレがあっても大体のスコアが分かったりします。 このスコアを計算する部分は Benchmark.js というライブラリで書かれていますので、サーバサイドの JavaScript コードの速度を計測する際にも利用できます。

どういう時に使うのか

JavaScript では全く同じ事をするのに複数の手段があるということがよくあります。 例えば、Array オブジェクトで数値を先頭から順に書き込んでいくとき、Indexer を使って代入する方法と push メソッドを使う方法があります。

こういった際に、テスト用の HTML + JavaScript を作ってポチポチとリロードして各ブラウザで実行した結果をメモ帳にまとめていっても良いのですが、 jsPerf では比較したい部分のコードを書くだけで簡単に比較することができます。

上記の例のベンチマーク

使用上の注意

コードスニペットの正当性はチェックしてくれないので、各コードスニペットが同じ結果を返すのかなどは予めチェックしておく必要があります。 以前出回った i++, ++i の速度比較など、 不公平な条件での結果をスコアだけみて「こっちの方がはやい!」などと思わないようにしましょう。 あまりに直感と掛け離れた結果は自分でいじってみるなど、疑ったり、理由を考える事も大切です。 ちなみに、上記の例ではベンチマーク対象データの生成を setup でやっておけば問題ありません。

JSPerfViewとは

jsPerf を使いだすと、ベンチマーク結果の共有を行いたい事が多々あります。
JSPerfView は HTML5 Rocks の canvas パフォーマンスチューニングのページで利用されている、jsPerf の計測結果をページに埋め込むためのものです。 上で表示されているグラフは JSPerfView を使って表示されています。 iframe を使って埋め込みます。 jsPerf の出力結果とは異なり、スニペットごとに各ブラウザの結果をまとめて表示します。(jsPerfでは各ブラウザごとにスニペットの結果をまとめて表示する)

簡単な使い方

プロジェクトページから何でも良いのでダウンロードしてきて、そのまま丸ごとどこか見れる場所にアップロードします。
自分は GitHub で fork してそのまま qh-pages ブランチに push して利用しています。

ベンチマーク結果を埋め込みたい場所で、以下のように iframe 要素に embed.html を表示したい jsPerf ベンチマークの Browserscope test ID 付きで指定する。

<iframe src="http://imaya.github.com/jsperfview/embed.html?id=agt1YS1wcm9maWxlcnINCxIEVGVzdBiPnYkODA"></iframe>

Browserscope test ID の確認方法

対象となる jsPerf のページを開き、計測結果のグラフの上の方にある "Browserscope" というリンクがあるので、これの URL の末尾が Browserscope test ID です。 例えば、以下の URL の強調した部分です。

http://www.browserscope.org/user/tests/table/agt1YS1wcm9maWxlcnINCxIEVGVzdBiPnYkODA
jsperf-browserscope-ss

バグとか表示方法の変更とか

グラフの表示がおかしくなる

現在、重い処理や貧弱なスマートフォンなどで実行して Ops/sec が 1 未満の結果を含む際にグラフがずれるようです。 この現象への対処は以下の変更を行う事で可能です。

diff --git a/js/chart.js b/js/chart.js
index e778b45..0281d4f 100644
--- a/js/chart.js
+++ b/js/chart.js
@@ -60,7 +60,7 @@ function parseChart_(response) {
       }
       // Compute series data
       var result = parseInt(platformResults[testName].result, 10);
-      if (result && !isNaN(result)) {
+      if (typeof result === 'number' && !isNaN(result)) {
         data.push(result);
       }
     }

全てのブラウザでの結果を表示したい

デフォルトのままでは、jsPerf でテストを行ったブラウザでも結果が表示されないものがあります。 これは、Browserscope の API でデータを取得する際にパラメータを省略すると "Top Browsers" のしか結果を返さないようになっているためです。 詳しくは Browserscope の API ページを参照してください。 全ての結果を表示するには以下のように変更します。

diff --git a/js/chart.js b/js/chart.js
index 2aa93f6..f682877 100644
--- a/js/chart.js
+++ b/js/chart.js
@@ -12,7 +12,7 @@ function Chart(testId) {
   this.chart = null;
 }
 
-Chart.URL_FORMAT = 'http://www.browserscope.org/user/tests/table/{ID}?o=json&callback=?';
+Chart.URL_FORMAT = 'http://www.browserscope.org/user/tests/table/{ID}?v=3&o=json&callback=?';
 Chart.MODERN = /Firefox ([4-9]|[1-2][0-9])|Chrome [1-2][0-9]|IE 9|IE 1[0-9]|Safari ([5-9]|1[0-9])/;
 Chart.MOBILE = /iPhone|iPad|Android/
 

グラフのサイズを変更したい

グラフが大きすぎる場合などサイズを変更したいことがあります。そういう場合は以下のように Highchart のオプションを指定してやれば変更できます。
ちなみに、iframe の height はこのグラフの大きさ + 40px くらいで指定するとちょうど良くなる事が多いようです。

diff --git a/js/chart.js b/js/chart.js
index b967797..6859bb8 100644
--- a/js/chart.js
+++ b/js/chart.js
@@ -87,7 +87,8 @@ function parseChart_(response) {
 function renderChart_(chartInfo, id, type) {
   chartInfo.chart = {
     renderTo: id,
-    type: type
+    type: type,
+    height: 300
   };
   chartInfo.yAxis = {
     title: {

JavaScript で書かれた ZLIB の伸張速度比較

はじめに

最近、Inflate 実装のチューニングを行うことが多かったので、現状でどの程度の速度が出ているか把握するため、他の実装と比較してみました。

比較に使用した ZLIB ライブラリ

今回の比較では、以下のライブラリの存在を確認しています。 uncompress.js に関しては、今回入手できなかったため比較対象からはずしています。

名前 Input Output 名前空間 ライセンス ファイルサイズ
pdf.js Uint8Array,
Array,
ArrayBuffer(*)
Uint8Array FlateStream,
Stream,
DecodeStream,
etc...
MIT stream.js: 80,349
zlib-js String String ZLIB zlib zlib-inflate.js: 86,884
zlib.js: 4,893
zlib.js Uint8Array,
Array
Uint8Array,
Array
Zlib MIT inflate.min.js: 13,222
uncompress.js Uint8Array,
Array
Uint8Array,
Array
zlib zlib uncompress.min.js: 11,843
jsziptools Uint8Array,
Array,
String,
ArrayBuffer
ArrayBuffer jz MIT jsziptools.min.js: 10,536(*)
dataview.min.js: 3,189
zpipe String String zpipe zlib? zpipe.min.js: 206,267

各ライブラリの簡単な特徴

pdf.js Typed Array のサポートが必要。
zlib-js Typed Array 未サポートの環境でも使用可能。
zlib の移植。
zlib.js Typed Array 未サポートの環境でも使用可能。
Stream 版(逐次展開)実装あり。
strict mode 対応。
Node.js 版あり。
Closure Library 対応。
出力バッファサイズの指定可能。
uncompress.js Typed Array 未サポートの環境でも使用可能。
zlib 1.2.5 の移植。
strict mode 対応。
出力バッファ指定可能。
現在サイトにつながらない?
jsziptools PKZIP 対応。
Typed Array のサポートが必要。
Inflate 実装は pdf.js の実装を使用。
ffDataView が必要。
zpipe zlib ライブラリに添付されている zpipe.c を emscripten で移植したもの。
Typed Array のサポートが必要。
Node.js 版あり。

分類について

見た感じ、大きく分けて3種類に分類することが出来ます。

  • zlib 移植系
    • zlib-js
    • uncompress.js
    • zpipe
  • pdf.js 系
    • pdf.js
    • jsziptools
  • 独自実装系
    • zlib.js

FlateStream について

FlateStream の入力は同じ stream.js 内で定義されている Stream のオブジェクトです。
new FlateStream(new Stream(array)) のような形で使います。array は Uint8Array のコンストラクタに渡せるものならば何でも OK です。
FlateStream を利用しているライブラリでは、この Stream を直接使うのではなく、getBytes メソッドを呼ぶ部分のコードを削って Uint8Array をそのまま渡すようにしていることが多いようです。

ビルドについて

ビルドスクリプトなどがついていて、Inflate の比較においてサイズ削減が可能なものに関してはすべて最低限で行っています。

jsziptools

jsziptools はビルドスクリプトが添付されていて、必要な実装のみでビルドすることが可能です。
今回は以下のようにして、zlib 伸張のみでビルドしました。

$ python build.py -m zlib.decompress

zlib.js

zlib.js では全ての実装を分離してビルドすることが出来、リポジトリの bin ディレクトリにそれぞれのファイルが生成されています。
今回は ZLIB の伸張なので inflate.min.js と inflate_stream.min.js をそのまま利用しています。

比較条件

速度比較に利用したデータ

http://www.compression.ca/act/act-files.html で使用しているデータの中からいくつかを Node.js の Zlib でデフォルト設定のまま圧縮したものです。
また、画像に関しては HTML5 Logo (PNG) から IDAT チャンクを抜き出したものを使用しています。

それぞれのライブラリで入力データの形式が違うので、データは予め String, Array, Uint8Array それぞれの形式に変換し、最も速度の出るものを利用しています。(純粋に伸張処理にかかった時間で比較しています。)
実際にライブラリで使用する際には、対応していない入力形式だった場合は変換コストも考慮して選ぶと良いかもしれません。

Adler-32 Checksum について

ほとんどの実装では Adler-32 によるチェックは行われないか、あるいはオプショナルであるため、今回の比較では行わないようにしています。

その他

比較結果について

Inflate は実装毎にバッファの管理方法が異なったりするため、展開するデータによって性能が上下することがあります。ここでの結果はあくまでも参考として利用した方が良いです。

zlib.js について

zlib.js では最新のコードは develop ブランチになっています。今回の比較では develop ブランチの実装を用いています。

ストリーム版 (inflate_stream.js) の結果も添付していますが、一度に全てのデータを流し込んでいるのであまり参考になりません。これは本来、断片的にデータが取得可能な場合に威力を発揮する実装です。

比較時の名前について

zlib-js と zlib.js は名前が似ているため zlib-js は iz-zlib と表記させていただきます。
すみません…。

比較結果

テキストデータ

The Three Musketeers

実行可能バイナリ

PINE

画像データ

HTML5 Logo (IDAT)

補足 (2012/08/15 - 23:22)

グラフの表示には jsperfview というものを使っているのですが、それで利用している Browserscope の API では最近のブラウザは表示されないようです。できるだけリンクをクリックして jsperf の方のグラフを見ることをおすすめします。

補足の補足 (2012/08/16 - 12:30)

jsperfview で利用している Browserscope の API パラメータを変更し、全てのブラウザの結果を表示するようにしました。

記事検索
最新コメント
最新トラックバック
カテゴリ別アーカイブ
タグクラウド
QRコード
QRコード
  • ライブドアブログ