追試：React + Performance = ?

# UPDATE@2016-01-26

MagJS のパフォーマンスを再度検証した結果、現時点では本番に投入できるレベルではないことが判明しました。

- [検証：MagJS の本当の描画パフォーマンス](http://qiita.com/t-suwa/items/dac3c929399185d2697f)

# はじめに

去年の年末に、React のパフォーマンスに関する興味深い記事を見つけました。

- [React + Performance = ? - Aerotwist](https://aerotwist.com/blog/react-plus-performance-equals-what/)

Google の Chrome 開発者関連チームで、パフォーマンスチューニングのコンサルを生業としている人が、React の[「DOM は遅い」](http://facebook.github.io/react/docs/multiple-components.html#a-note-on-performance)という主張にカチンと来て、本当にそうなのかどうかを検証してます。

その結果、DOM は遅くないということが証明され（ある意味、確信犯的にテストしているところもあったので、当然と言えば当然なのですが）、また、React の[「デフォルトで速い」という主張](https://facebook.github.io/react/docs/perf.html)については、デスクトップ版の Chrome ではそこそこ健闘しているものの、Nexus 5 ではかなり不安要素を残すことがわかりました。詳しくは、元記事をご覧ください。

去年の 7 月に書かれたこの記事は React の大流行に一石を投じるその内容にも関わらず、Qiita ではこれまで誰も取り上げていないようです。

じゃあ抄訳でも書くかと思ったのですが、元記事が書かれてから半年も経過しているため、状況が変化している可能性は否めません。そこで、MagJS のテストも加えて追試してみることにしました。

コードはオリジナルからフォークして、[ここに置いてあります](https://github.com/t-suwa/samples/tree/gh-pages/perf/dom-react-vs-vanilla)ので、さらに追試したい人は是非。

# テスト内容

React の「JavaScript は速くて、DOM は遅い」という主張が本当かどうかを検証するために、無限リストでパフォーマンスの変化を見ます。ボタンをクリックする度に Flickr から画像を 100 枚取得して、ページの末尾に追加していきます。

当然のことながら、画像を取得する時間はネットワークの状況に大きく左右されるため、計測時間からは除外されます。純粋に、画像の取得が完了してから DOM の更新が完了するまでの時間が計測の対象です。DOM の更新完了は、次の requestAnimationFrame が呼ばれるまでの時間でラフに計測します。

なお、オリジナルでは、1,200 枚までテストしてますが、今回はさらに傾向を見るために 1,500 枚までテストしました。

素の JS と React のテストコードはオリジナルのままです。ただし、React 本体は執筆時の最新版である v0.14.6 にアップデートしたものもテストしました。この半年で、React のパフォーマンスが改善している可能性を考慮しています。

環境は、iMac Late 2009 / OS X El Capitan。今となっては、やや古くさいですね。Chrome はバージョン 47.0.2526.106 (64-bit) で、現時点の最新。オリジナルのテストでは Nexus 5 でのテストも実施してますが今回は割愛してます。興味がある人は自分でテストしてみて下さい。

# React の結果

## v0.13.3

オリジナルのテストと同じバージョン。150ms から 330ms にかけて、ほぼ右肩上がりです。

青のラインは純粋な JavaScript の処理にかかった時間で、赤のラインは最終的に DOM の更新が完了した時間です。この青と赤の線の隙間が、すなわち DOM の更新にかかった時間を表しています。

![React v0.13.3.png](https://qiita-image-store.s3.amazonaws.com/0/65212/ea495c42-7abb-b3c5-0a8d-2e90c1343594.png)

この幅がほぼ一定なのは、React が差分検出を行うことで DOM の更新回数（箇所）が一定に保たれていることの証明にもなっています。そうでなければ、写真枚数に応じて赤のラインがどんどん乖離していくはずです。

## v0.14.6

右肩上がりの傾向は変わりませんが、だいぶパフォーマンスが改善されているように見えます。

![React v0.14.6.png](https://qiita-image-store.s3.amazonaws.com/0/65212/0fd112e2-a3e3-4b61-100c-8e8e6eb6726e.png)

繰り返しテストを実施すると、特に写真枚数が少ない時のパフォーマンスが安定して良かった印象があります（v0.13.3 と比較して）。

# 素の JS の結果

若干乱れはあるものの、ほぼ、React の 1/2 程度の処理時間で済んでます。オリジナルの記事では 1,200 枚のところで不可解な落ち込み（大幅なパフォーマンスの改善）が観測されていますが、このテストでは比較的素直な右肩上がりです。

![Vanilla.png](https://qiita-image-store.s3.amazonaws.com/0/65212/e986bed2-cf72-2267-8c59-04e5266e4a87.png)


# MagJS の結果

Y 軸のスケールに注目。桁が違います。MagJS については慎重に何度もテストを実施しましたが、ほぼ同様のグラフです。

![Mag v0.21.3.png](https://qiita-image-store.s3.amazonaws.com/0/65212/2e18c9f8-fdb2-6875-31aa-c54a0c5f49a5.png)

正直なところ、MagJS のパフォーマンスを正確に計測できているのかまだ確信が持てないので、この結果はあまり信用しないでください。

# 全体の比較

最後に、全体の処理時間を比較したグラフがこちら。

![TotalTime.png](https://qiita-image-store.s3.amazonaws.com/0/65212/71c3dd7d-d6f7-0f9d-4f75-ae0991ac3dd6.png)

React のパフォーマンスは改善されているものの、傾向としては半年前と同様です。

# 補足

元記事ではテストに先立ち、「抽象化はコストに見合うのか？」という問いを立てていました。

> In any case the question is not “Is React faster than
Vanilla?”, so much as it is about how much extra time React
is going to take to run, and thereby affect the user 
experience. In short: is the abstraction worth the cost?

React が素の JS より速いかどうか、ということは問いにはなりえません。なぜなら、

> It strikes me that even if JavaScript itself is fast, there are two trees that React is going to have to diff to make some patches, and therefore going direct will be faster, provided I don’t trigger layout thrashing or some other horror.

だからです。裏 DOM を持って差分検出を走らせる以上、処理時間ではどうあがいても素の JS に勝つことなどできないと。当然の話ですね。

そして元記事では最後にこう締めくくっています。

> It seems to me that developer ergonomics should be less important than our users’ needs, as painful as that can be for us developers. Despite the claims, React does seem to have significant performance implications, at least under certain circumstances.

正直、これを読んだ時にハッとしました。確かに、抽象化を行い開発効率を高めることで開発者はハッピーになるかもしれないけれど、それでユーザー利益が損なわれるとしたら、本末転倒なことです。今回テストを行った理由も、このくだりで少なからず我が身を反省したところにあります。

MagJS についてはまだ自分自身で納得がいっていないので判断は保留します。もう少し深掘りが必要な状況です。

# まとめ

抽象化にはコストが伴います。そのコストがユーザーに転嫁されているとしたら、憂慮すべき事態です。特に、モバイル端末でのパフォーマンス悪化はユーザー離れに直結するでしょう。

せっかく導入したイケてる抽象化が、サービスのパフォーマンスダウンを引き起こし、競争力を低下させるとしたら、こんなブラックジョークはない。

そして、いざパフォーマンス改善に取り組まなければならなくなったときに、それまでに積み上げてきた抽象化が、技術的負債となってのしかかるかもしれません。

誤解しないで欲しいのですが、この記事の目的は React を貶めることではないし、ましてや MagJS を勧めることでもありません。

元記事にあるように、自分の手でテストをして、その結果から判断を下していくプロセスの大切さです。

その結果、React のパフォーマンスを許容範囲だと思って使うのもありだし、無限リストのページだけ React を使わないようにする、といったハイブリッドな実装にするのもアリでしょう。

Flux も、もはや自転車置き場の議論のようになってますが、そこで成し遂げられるナイスな抽象化によってどこにコストが発生するか、という視点が置き去りになっているような印象を受けます。

書きやすさだけに着目せず、様々な角度から点検する態度を、忘れないようにしたいなと、改めて思いました。自戒を込めて。