13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

検証:MagJS の本当の描画パフォーマンス

Posted at

はじめに

前回の記事で、MagJS の計測結果についてはまだ納得できていないと書きました。

そこで検証方法を見直し、MagJS の本当の描画パフォーマンスについてほぼ納得のいく結果を得られたので、ここに記事として共有します。

前回の振り返り

MagJS は requestAnimationFrame(以降、rAF と略す)を使って描画処理を非同期化します。rAF に渡された関数が実行されるのは、ブラウザが次の再描画をする直前です。再描画のタイミングは 60fps を基準にディスプレイのリフレッシュレートと同期するように調整されます。

rAF を使うと描画処理がペンディングされ、すぐに制御が戻ってくるため、見かけ上のパフォーマンスはよく見えます。前回の計測結果はこの部分を見ていたので、桁違いのパフォーマンスとなっていました。しかし、そこには肝心の描画処理が含まれていません。

本当に知りたいのは、rAF に渡した関数の実行時間です。

どうやったらこれを計測することができるのか。そのヒントを得るために、再び MagJS の描画ロジックを確認します。

makeRedrawFun - 再描画関数を作成する関数

rAF に渡す描画関数は、makeRedrawFun で作ります。

mag.js抜粋
  var makeRedrawFun = function(node1, idInstance1, force1) {
    return function(node, idInstance, force) {

      getNode(node.id, 1)

      // verify idInstance
      if (!isValidId(node.id, idInstance) || !node) {
        return
      }

      var state = mag.mod.getState(idInstance)

      // LIFE CYCLE EVENT
      if (mag.utils.callLCEvent('isupdate', state, node, idInstance)) return;

      var props = mag.mod.getProps(idInstance)

      var data = mag.utils.merge(mag.utils.copy(props), mag.utils.copy(state))

      // CACHED?
      if (mag.mod.iscached(idInstance, data) && !force) {
        return 0;
      };


      // LIFE CYCLE EVENT
      if (mag.utils.callLCEvent('willupdate', state, node, idInstance)) return;


      //RUN VIEW FUN
      mag.mod.callView(node, idInstance);

      //START DOM
      mag.fill.setId(node.id)
      mag.fill.run(node, state)
      // END DOM

      //CONFIGS
      callConfigs(node.id, mag.fill.configs)

      // add configs unloaders
      addConfigUnloaders(node.id, idInstance)

      // LIFE CYCLE EVENT
      mag.utils.callLCEvent('didupdate', state, node, idInstance)

      // get parent to call
      if(node && node.parentNode){
        var parent = findClosestId(node.parentNode)
        if(parent) {
          mag.redraw(parent, mag.utils.items.getItem(parent.id))
        }
      }
      
    }.bind({}, node1, idInstance1, force1)
  }

この流れを簡略化すると以下のようになります。

  1. ライフサイクルイベントの willupdate を呼ぶ
  2. mag.mod.callView で view 関数を呼ぶ
  3. mag.fill.run で view 関数の結果を DOM に反映する
  4. _config フックを呼ぶ
  5. ライフサイクルイベントの didupdate を呼ぶ

というわけで、willupadte から didupdate までを計測すれば、描画処理のパフォーマンスがほぼわかると言って良さそうです。

素の JS や React のテストとは計測方法が異なってしまうものの、これ以上妥当なやり方はおそらくないので、これで進めることにします。

MagJS の描画の最適化

MagJS は Object.observe でデータ変更を検出し、自動的に再描画を行います。このため、再描画処理中に this のプロパティに代入を行ったりすると、再帰的に再描画が走ります。

今回のテストでも不用意なコーディングで再描画が走っている個所があったので、これを見直しています。

計測結果

お待ちかねの計測結果はこちら。環境は前回と同じく、iMac late 2009 です。

magjs-v0.21.3.png

驚くほど急激な右肩上がりのグラフです。これは、MagJS が DOM の差分検出していないことが多分に影響していると推測できます。率直に言って、かなり悪い数字です。

全体の比較

前回の計測結果と今回の結果をマージしたグラフです。

totaltime.png

これを見ると、もはや MagJS はお話にならないくらいパフォーマンスが悪いということがわかります。

まとめ

MagJS のパフォーマンスを計測し直した結果、少なくとも現時点では、本番に耐えうるパフォーマンスではないということが明確になりました。残念ながら、MagJS は看板に偽りあり、と言わざるを得ません。

スーパークレイジーに速いと喧伝されていた各種パフォーマンステストの結果も、結局のところ、肝心の描画処理は省かれていた訳です。

自らの不明を恥じるとともに、今回の検証で少しでも間違った知見を訂正できればと思います。

MagJS 版の TodoMVC について解説記事を書く予定だったものの、こんな状況になってしまった以上もはやそこに意味はないため、次は Mithril のパフォーマンステストでもやってみようかと思っています。

13
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?