はじめに
前回の記事で、MagJS の計測結果についてはまだ納得できていないと書きました。
そこで検証方法を見直し、MagJS の本当の描画パフォーマンスについてほぼ納得のいく結果を得られたので、ここに記事として共有します。
前回の振り返り
MagJS は requestAnimationFrame
(以降、rAF
と略す)を使って描画処理を非同期化します。rAF
に渡された関数が実行されるのは、ブラウザが次の再描画をする直前です。再描画のタイミングは 60fps を基準にディスプレイのリフレッシュレートと同期するように調整されます。
rAF
を使うと描画処理がペンディングされ、すぐに制御が戻ってくるため、見かけ上のパフォーマンスはよく見えます。前回の計測結果はこの部分を見ていたので、桁違いのパフォーマンスとなっていました。しかし、そこには肝心の描画処理が含まれていません。
本当に知りたいのは、rAF
に渡した関数の実行時間です。
どうやったらこれを計測することができるのか。そのヒントを得るために、再び MagJS の描画ロジックを確認します。
makeRedrawFun
- 再描画関数を作成する関数
rAF
に渡す描画関数は、makeRedrawFun
で作ります。
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)
}
この流れを簡略化すると以下のようになります。
- ライフサイクルイベントの
willupdate
を呼ぶ -
mag.mod.callView
で view 関数を呼ぶ -
mag.fill.run
で view 関数の結果を DOM に反映する -
_config
フックを呼ぶ - ライフサイクルイベントの
didupdate
を呼ぶ
というわけで、willupadte
から didupdate
までを計測すれば、描画処理のパフォーマンスがほぼわかると言って良さそうです。
素の JS や React のテストとは計測方法が異なってしまうものの、これ以上妥当なやり方はおそらくないので、これで進めることにします。
MagJS の描画の最適化
MagJS は Object.observe
でデータ変更を検出し、自動的に再描画を行います。このため、再描画処理中に this
のプロパティに代入を行ったりすると、再帰的に再描画が走ります。
今回のテストでも不用意なコーディングで再描画が走っている個所があったので、これを見直しています。
計測結果
お待ちかねの計測結果はこちら。環境は前回と同じく、iMac late 2009 です。
驚くほど急激な右肩上がりのグラフです。これは、MagJS が DOM の差分検出していないことが多分に影響していると推測できます。率直に言って、かなり悪い数字です。
全体の比較
前回の計測結果と今回の結果をマージしたグラフです。
これを見ると、もはや MagJS はお話にならないくらいパフォーマンスが悪いということがわかります。
まとめ
MagJS のパフォーマンスを計測し直した結果、少なくとも現時点では、本番に耐えうるパフォーマンスではないということが明確になりました。残念ながら、MagJS は看板に偽りあり、と言わざるを得ません。
スーパークレイジーに速いと喧伝されていた各種パフォーマンステストの結果も、結局のところ、肝心の描画処理は省かれていた訳です。
自らの不明を恥じるとともに、今回の検証で少しでも間違った知見を訂正できればと思います。
MagJS 版の TodoMVC について解説記事を書く予定だったものの、こんな状況になってしまった以上もはやそこに意味はないため、次は Mithril のパフォーマンステストでもやってみようかと思っています。