※効果には個人差があります。
useMemoのオーバーヘッドについて
ReactのuseMemo
は、パフォーマンス最適化に使われるAPIです。コンポーネント内で計算やオブジェクトの生成を行う際に、以前の計算結果をキャッシュして使い回すことで再レンダリング時の計算を削減したり、新しいオブジェクトの生成を防ぐことができます。
useMemo
に関しては、あくまで最適化のためのものであるから「無駄に使うべきではない」という言説がよく見られます。その理由は、useMemo
のコストもゼロではなく、余計な使用はそれだけパフォーマンスの低下に繋がってしまうからです。
しかし、筆者はuseMemo
のコストは微々たるものであり、本当に一目見て明らかに無駄でない限りは積極的に使うべきだと思っています。
そこで、筆者はuseMemo
のオーバーヘッドがどれくらいかを調べるためのベンチマークを作成しました。この記事ではその結果を共有します。結論はタイトルにある通りです。以前にも以下の記事で同じような主張をしていたのですが、そのときはベンチマークが適当で精度が低かったのでこの記事では以前よりはちゃんとしたベンチマークを用意しました。
ベンチマーク
ここで試すことができます。適当なコンポーネントを何度もレンダリングしてその時間を計測し、最小値・最大値・25%/50%/75%/99%パーセンタイルを表示します。計測対象としては、余計なuseMemoがない場合・余計なuseMemoがある場合に加えて、useMemoと同数の余計な<div />
がある場合を計測してみました。
3つの設定項目があります。
-
Render Timing: コンポーネントの初回レンダリング時の時間を計測するのか、再レンダリング時の時間を計測するのかを選択します。デフォルトでは再レンダリングになっています。どちらの結果を重視するかは場合によりますが、再レンダリング時の時間を計測する方が余計な
<div />
がある側に有利です。 - Number of useMemo/div per rendering: 1つのコンポーネントに何個の余計なuseMemo/divを含むかです。
- Number of renderings: 計測を何回行うかです。
下2つのデフォルト値は筆者のMacBook Proでちょうどいい時間(10秒強)で終わるように設定されていますので、適宜調整してください。
ベンチマーク結果
筆者の環境での結果を共有します。(再レンダリング, M=10000, N=1000)
Time spent for one rendering (ms):
min | 25% | 50% | 75% | 99% | max | |
---|---|---|---|---|---|---|
raw | 0.00 | 0.10 | 0.10 | 0.10 | 0.30 | 0.70 |
useMemo | 1.50 | 1.70 | 1.80 | 1.80 | 4.70 | 7.10 |
div | 2.70 | 3.00 | 3.10 | 3.20 | 4.20 | 6.70 |
50パーセンタイル(中央値)に注目してみましょう。余計なuseMemoが10,000個ある場合、1.7ミリ秒程度のオーバーヘッドがあるようです。これはMac上での結果なので、スマートフォン対象の超大規模アプリだと10ミリ秒単位の差が出るかもしれません。1個あたりだと0.00017ミリ秒くらいのオーバーヘッドと概算できますね。
さらに、驚くべきことに、余計なdivが10,000個ある場合は3.0ミリ秒程度のオーバーヘッドが観測されています。useMemoの約1.76倍です。
useMemoの方が大きな外れ値が出る傾向がありますが(何回か計測したり他の人の結果を見ても同様でした)、全体を見ればuseMemoよりもdivの方が実は高コストであることが明らかになりましたね。
まとめ
塵も積もれば山となると言いますから、余計なuseMemoを削ってコストを削減するのは悪いことではないかもしれません。
しかし、useMemoひとつのオーバーヘッドよりもdivひとつのオーバーヘッドのほうが大きいのです。よって、余計なuseMemoを削ることにご執心の場合はその1.7倍の情熱を余計なdivを削ることに注がなければ道理に合わないのです。
一応述べておきますが、筆者の主張は「useMemoもdivも削れ」ではありません。「微々たるものだからどちらも気にしなくていい。しかしuseMemoを気にするのにdivを気にしないのはおかしい」というのが筆者の考えです。
なお、ベンチマークのコードはGitHubで公開していますので、手法や結果に異論がある場合はぜひコメント欄などで議論してください。
続編ができました↓
Q&Aコーナー
Q. 積極的にuseMemoを使うべき理由が書いていない
A. useCallbackについては以前の記事で書きました。useMemoでも本質はだいたい同じです。この記事はuseMemoを積極的に使いたい人を応援する記事なので、積極的に使いたいかどうかはあなた次第です。
Q. 速度より消費メモリ量を見るべきでは?
A. ウェブ開発でメモリ量を気にする人がぜんぜんいないし速度に比べて重要性が低いので速度を測りました。ただ、useMemoで外れ値が大きく出るのはGCっぽい気がする(憶測)
A. (ここに対するつっこみが多いので回答を訂正しました) 消費メモリ量の制御が重要な場面が存在することは否定しませんが、速度に比べて重要ではない場面が多いこともやはり否定できません。少なくともこの記事は後者の場合に役に立つでしょう。消費メモリ量が重要な場合は残念ですがこの記事の内容はあまり参考にならないかもしれません。
Q. 環境が書いていない。環境によって結果が違うのでは?
A. 筆者のMacBook Pro 2018上のGoogle Chrome 96です。ご指摘の通り環境によって結果が違うのは当たり前です。ただ、useMemoとdivの相対的な倍率についてはあまり変わらないのではないかと思い積極的に喧伝しています。