簡単アニメーション！Pixi.jsを触ってみる！（4）パフォーマンスを測定してみた

# 簡単アニメーション！Pixi.jsを触ってみる！<br>〜（4）パフォーマンスを測定してみた〜

Pixi.jsは **速い！** と定評の簡単にアニメーションが作れるライブラリです。

<a href="http://www.pixijs.com/"><img src="http://img.dfm.lrv.jp/pixi4/img/pixilogo.png"></a>
[pixi.jsの公式サイトへ](http://www.pixijs.com/)

これまで亀のような歩みで使い方を覚えてきましたが、今回は中休み（ネタ考えるのが面倒w）ということでパフォーマンスを測定してみました。

パフォーマンス測定といえば、もうずいぶん前に、[ICS池田さんの記事](http://ics-web.jp/lab/archives/201)でも、ぶっちぎりの結果が紹介されていて、この記事を見て「pixi.jsしかないじゃん！」と、あっさりとprocessing.jsから乗り換えた思い出があります。

他のjsライブラリとの比較については、池田さんの記事を参照くださいね。

## どのくらいの機種で動くのか知りたい!

今回の調査で知りたかったのは、 **pixi.jsはどのくらいの機種や環境で動くの？** という点です。
なので、手持ちのPCや某所にある機種も借りて、いろいろ見てみました。
（一人では無理なので、手伝ってもらいました。測定にご協力いただいたIさん、ありがとうございます！）

- iPhoneやiPad (たまたまあったもの)
- Android端末 (たまたまあったもの)
- その他PCなど (たまたまあったもの)

つまり、評価機種の選定基準は **たまたまあったもの** ですw

> なぜかAndroid機種が某キャリアに偏っているように見えますが、気のせいです。たぶん。

しかも、pixi.jsしか見ていません！めちゃくちゃ偏りがある点はご容赦ください。

## テストプログラム

テストには前回記事の最後に作った[コレ](http://img.dfm.lrv.jp/pixi3/sample10/)をもとに、表示するスプライト数を増やして1秒あたりの描画回数を取得できるようにしたものを使いました。

おもいっきり手抜きですが、目的は果たせるはず！！（と自分に言い聞かせるw）

- [スプライト数10](http://img.dfm.lrv.jp/pixi4/bench10/)
- [スプライト数50](http://img.dfm.lrv.jp/pixi4/bench50/)
- [スプライト数100](http://img.dfm.lrv.jp/pixi4/bench100/)
- [スプライト数500](http://img.dfm.lrv.jp/pixi4/bench500/)
- [スプライト数1000](http://img.dfm.lrv.jp/pixi4/bench1000/)
- [スプライト数5000](http://img.dfm.lrv.jp/pixi4/bench5000/)
- [スプライト数10000](http://img.dfm.lrv.jp/pixi4/bench10000/)

コードはこんな感じになりました。(テストするスプライト数に応じて`spritesMax`を変更しています。)

```js
<script>
var spritesMax = 10; // 表示するスプライト数。

var width = 600;
var height = 400;

// ベンチマーク用
var firsttime;
var bCount = 0;
var cps = 0;
var cpsresults = [];
var testtimes = 0;
var nowtime;
var oldtime;

// ステージ・レンダラーを作ってDOMに配置
var stage = new PIXI.Stage('black');
var renderer = PIXI.autoDetectRenderer(width, height);
document.getElementById("pixiview").appendChild(renderer.view);

// コンテナを2つつくる。(for文かよw)
var containers = [];
for (var i=0;i < 2; i++){
  containers[i] = new PIXI.DisplayObjectContainer();
  containers[i].position.x = 100+(i*300);
  containers[i].position.y = 50+(i*100);
  stage.addChild(containers[i]);
}

// 画像からスプライトオブジェクトを作る
var framenames = [
  "test_r1_c1.png",
  "test_r1_c2.png",
  "test_r1_c3.png",
  "test_r1_c4.png",
  "test_r2_c1.png",
  "test_r2_c2.png",
  "test_r2_c3.png",
  "test_r2_c4.png"];
var sprites = [];
var speed = [];

// Asset Loaderでスプライト・シートをテクスチャフレームに読み込む
loader = new PIXI.AssetLoader(["img/test.json"]);
loader.onComplete = onComp; // 読み込みは非同期で行われます
loader.load(); // ロード開始!

// ロード後の処理 (非同期でコールバックされます)
function onComp()
{
  // テクスチャフレームからスプライトを作ってステージに並べます。
  for(var cnt=0;cnt < spritesMax;cnt ++){
    var name = framenames[cnt % (framenames.length)];
    sprites.push(PIXI.Sprite.fromFrame(name));
    sprites[cnt].position.x = Math.random() * 600;
    sprites[cnt].position.y = Math.random() * 400;
    sprites[cnt].anchor.x = 0.5;
    sprites[cnt].anchor.y = 0.5;

    // 前半と後半を分けてコンテナに入れる。
    if((cnt % 2) == 0){
      containers[0].addChild(sprites[cnt]);
    }else{
      containers[1].addChild(sprites[cnt]);
    }

    var t =+new Date();
    firsttime = Math.floor(t/1000);
    speed.push(Math.random());
  }

  // 次のアニメーションフレームでanimate()を呼び出してもらう
  requestAnimFrame(animate);
}

var sel1 = 0;
var sel2 = 1;
var counter = 0;

// アニメーション関数
function animate(){
  bCount ++;
  var nt = +new Date();
  nowtime = Math.floor(nt/1000);

  if(nowtime != oldtime){
    cps = bCount;
    bCount = 0;
    oldtime = nowtime;
    document.getElementById("cps").innerHTML=cps;
    if(nowtime > (firsttime + 3)){
      if(testtimes < 10){
        cpsresults.push(cps);
      }else{
        var total = 0;
        for(var i=0;i<cpsresults.length;i++){
          total += cpsresults[i];
        }
        var ave = Math.floor(total/cpsresults.length);
        document.getElementById("ave").innerHTML=ave;
      }
      testtimes ++;
    }
  }
  requestAnimFrame(animate); // 次の描画タイミングでanimateを呼び出す
  for(cnt=0;cnt <sprites.length;cnt++){
    sprites[cnt].rotation += (speed[cnt] / 10);
    sprites[cnt].position.x += (speed[cnt]*10);
    if(sprites[cnt].position.x > 700){
      sprites[cnt].position.x = -200;
      speed[cnt] = Math.random();
      sprites[cnt].position.y = Math.random() * 400;
    }
  }
  // containerを変形させる処理
  if(counter > 100){
    sel1 ^= 1;
    sel2 ^= 1;
    counter = 0;
  }
  containers[sel1].scale.x += 0.015;
  containers[sel1].scale.y += 0.005;
  containers[sel2].scale.x -= 0.015;
  containers[sel2].scale.y -= 0.005;
  renderer.render(stage);   // 描画する
  counter ++;
}
</script>
```

## 測定方法

テストサイトを表示してしばらく待つと **(Ave):** の横に数字が出てきます。
これが、1秒間のに表示できた回数の平均値(10回やって割ってる)です。

各端末ごとに、10000→5000→1000→500→100→50→10
の順にテストを行って、結果を記録しました。

ただ、途中で59点以上が出たら、それより少ないスプライトでは60点以上出るはずなので、
それ以降は行わず、見なしで60点をつけました。
また、1秒間に30回以下だと、スムーズなアニメと言えないので得点に入れないことにしました。

![測定の様子(ウソ)](http://img.dfm.lrv.jp/pixi4/img/im.png)

## 測定結果（得点）

まず、単純に得点を計算してみました。
一応、iOS、Android、その他のように並んでいるので、あえてソートしていませんが、下記のようになりました。
スコアの数え方は、テストしたスプライト数×得点 の合計です。

|機種名 （バージョン）|スコア|
|:----|----:|
|iPhone3GS (4.3) Safari|120|
|iPhone4S (5.1.1) Safari|9600|
|iPod touch 5G (6.0.1) Safari|25100|
|iPhone5 (7.0.3) Safari|77100|
|iPhone5C (7.0.6) Safari|77100|
|iPhone5S (7.0.4) Safari|90600|
|iPad2 (5.1.1) Safari|9600|
|iPad 3rd (6.1.3) Safari|28100|
|IS03 (2.2.1) ブラウザ|270|
|IS11SH (2.3.3) ブラウザ|380|
|IS11PT (2.3.4) ブラウザ|300|
|IS11N (2.3.5) ブラウザ|400|
|KYY04 (4.0.3) ブラウザ|7500|
|KYY04 (4.0.3) Chrome 25.0|90|
|HTL21 (4.1.1) ブラウザ|26200|
|HTL21 (4.1.1) Chrome 30.0 |4900|
|LGL23 (4.2.2) ブラウザ|84600|
|LGL23 (4.2.2) Chrome 28.0|6150|
|SCL22 (4.3.0) ブラウザ|79100|
|SCL22 (4.3.0) Chrome 33.0|274600|
|NEXUS7 (4.4.2) Chrome 33.0|99600|
|iMac (10.7.5) Safari 6.0.5|99600|
|Macbook Air 2013 Chrome 34.0|909600|
|Windows 7 Desktop Chrome 33.0 |739600|
|Windows 8.1 Note IE 11.0|729600|
|geeks phone keon (FireFox OS)|330|

上記のようにAndroid2.x系は全滅でした。
今回、会社にたまたま転がってた話題のFireFox OS端末でも見てみたのですが、これもAndroid2.x系と同じような印象。
面白かったのが、AndroidのChrome。25だと酷い結果なんですが、30以上だと速くなります。
WebGLがデフォルトで有効になっているのが効いているのではないでしょうか。
上の表に載せるまでもなく、途中でやる気がなくなったのがIE9。Core i5マシーンで試したにも関わらず。。。はっきり言ってまともに動きませんw ガタガタです。FireFox OS端末の方が幾分スムーズに動く、といったレベルでした。

## 測定結果（どのくらいのスプライトがスムーズに動いた？）

先ほどのまとめ方だと、 **結局、どのくらいのスプライト数までスムーズに動くの？** という点がよくわかりません。
そこで機種別に、50フレーム以上で動いた一番高負荷なテスト（=スプライト数）を出してみました。

|機種名 （バージョン）|スムーズに動いた一番一番高負荷なテスト|
|:----|---:|
|iPhone3GS (4.3) Safari|-|
|iPhone4S (5.1.1) Safari|100|
|iPod touch 5G (6.0.1) Safari|100|
|iPhone5 (7.0.3) Safari|500|
|iPhone5C (7.0.6) Safari|500|
|iPhone5S (7.0.4) Safari|1000|
|iPad2 (5.1.1) Safari|100|
|iPad 3rd (6.1.3) Safari|100|
|IS03 (2.2.1) ブラウザ|-|
|IS11SH (2.3.3) ブラウザ|-|
|IS11PT (2.3.4) ブラウザ|-|
|IS11N (2.3.5) ブラウザ|-|
|KYY04 (4.0.3) ブラウザ|50|
|KYY04 (4.0.3) Chrome 25.0|-|
|HTL21 (4.1.1) ブラウザ|100|
|HTL21 (4.1.1) Chrome 30.0 |10|
|LGL23 (4.2.2) ブラウザ|500|
|LGL23 (4.2.2) Chrome 28.0|50|
|SCL22 (4.3.0) ブラウザ|500|
|SCL22 (4.3.0) Chrome 33.0|1000|
|NEXUS7 (4.4.2) Chrome 33.0|1000|
|iMac (10.7.5) Safari 6.0.5|1000|
|Macbook Air 2013 Chrome 34.0|10000|
|Windows 7 Desktop Chrome 33.0 |5000|
|Windows 8.1 Note IE 11.0|5000|
|geeks phone keon (FireFox OS)|-|

こうして見ると、まぁ、ちゃんと動かない機種・環境はどうしようも無いとして、スマートフォン向けだとスプライト数100くらいで作った方が良いと思いました。

理由は、

> 私のiPod touch 5Gでうごかしたい！wwwwwwww

からです！
（なんだか、これ、最後のiPod touchになりそう。大事にしたい。。。）

## 測定結果（生データ）

見たい人はいないと思いますが、将来の自分のために、テスト結果の生データを下記に置いておきます。
[生データ](http://img.dfm.lrv.jp/pixi4/score_raw.xls.zip)

## まとめ。次回は「インタラクションに挑戦！」

いろいろな機種で動かしてみましたが、やはり古めの機種ではたくさんのスプライトを動かすのは辛いようです。
新しめの機種では、1000スプライトでもギュンギュン動きます。これは楽しいです。
これからギュンギュン動く機種ばかりになると予想されるので、さっさと使えるようになりたいと改めて思いました。

- iPod touch 5Gは重要！スプライトは100くらいにしとけ（最重要）
- MacのChromeとかだと画面リフレッシュレートとrequestanimationframeのコールバックがたぶん同期してて60fps以上にならないんだけど、iPhoneのSafariとかだと、64とかになることも。本当に画面のリフレッシュレートと同期してるか怪しい気がする。 
- スマートフォンは2年くらい前の機種だと結構つらい。AndroidのVersionというよりハードウエアスペックな気がする。
- Androidだと4.1、iOSだと5がプリインされてたくらいの機種からそれなりに動く感じ。
- AndroidのChromeもVersion 30以前だと厳しくなる（設定でWebGLを有効にできるらしいけど、コンテンツ作る人はそこに期待できないので）
- IEは9はダメダメでしたが、11は普通に速かった。(10はやってない)

さて、次回はいよいよ、 **ユーザー操作に反応する何か** に挑戦してみたいと思います！

お楽しみに！