JavaScript
reactjs
MagJS

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

More than 3 years have passed since last update.


はじめに

前回の記事で、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 のパフォーマンステストでもやってみようかと思っています。