最適化とベストプラクティス
効果的にスマートポインタを使う
注意: このセクションで使われている用語がわからなければ Rust book は スマートポインタについての章 があり、非常に有用です。
再レンダリング時にpropsを作成するために大量のデータをクローンすることを避けるために、
データそのものではなく、データへの参照のみをクローンするスマートポインタを使用できます。
実際のデータではなく、propsと子コンポーネントで関連するデータへの参照を渡すことで、
子コンポーネントでデータを変更する必要があるまでデータのクローンを避けることができます。
その場合、Rc::make_mut
を使用してクローンし、変更したいデータへの可変参照を取得できます。
これにより、propの変更がコンポーネントの再レンダリングを必要とするかどうかを判断する際に、
Component::changed
でさらなる利点がもたらされます。これは、データの値を比較する代わりに、
基礎となるポインタアドレス(つまり、データが格納されているマシンのメモリ上の位置)を
比較できるためです。2つのポインタが同じデータを指している場合、それらが指すデータの値は
同じでなければなりません。ただし、逆は必ずしも真ではないことに注意してください!
2つのポインタアドレスが異なっていても、基礎となるデータは同じかもしれません。
この場合、基礎となるデータを比較する必要があります。
この比較を行うには、PartialEq
(等値演算子==
を使用してデータを比較する際に自動的に使用される)を
使用するのではなく、Rc::ptr_eq
を使用する必要があります。Rustのドキュメントには
Rc::ptr_eq
についての詳細があります。
この最適化は、Copy
を実装していないデータ型に最も有用です。データを安価にコピーできる場合、
スマートポインタの背後に置く価値はありません。Vec
、HashMap
、String
のようにデータが重くなる可能性のある
構造体の場合、スマートポインタを使用するとパフォーマンスの改善が見込まれます。
この最適化は、値が子によって決して更新されない場合に最も効果的であり、
親によってもめったに更新されない場合はさらに効果的です。これにより、Rc<_>
は
ピュアコンポーネントのプロパティ値をラップするのに適した選択となります。
ただし、子コンポーネントでデータを自分でクローンする必要がない限り、 この最適化は無用であるだけでなく、参照カウントの不必要なコストも追加することに注意する必要があります。 Yewのpropsはすでに参照カウントされており、内部的にデータのクローンは発生しません。
ビュー関数
コードの可読性のために、html!
のセクションを独自の関数に移行することはしばしば意味があります。
これにより、インデントの量が減るためコードがより読みやすくなるだけでなく、
良い設計パターンも促進されます。特に、これらの関数は複数の場所から呼び出すことができるため、
記述する必要のあるコードの量が減り、構成可能なアプリケーションの構築に役立ちます。
ピュアコンポーネント
ピュアコンポーネントは、状態を変更せず、コンテンツを表示し、
通常の可変コンポーネントにメッセージを伝播するだけのコンポーネントです。
ビュー関数とは異なり、式構文(({some_view_function()}
))ではなく
コンポーネント構文((<SomePureComponent />
))を使用してhtml!
マクロ内から使用でき、
実装によってはメモ化できます(これは、関数が一度呼び出されるとその値が「保存」され、
同じ引数で複数回呼び出された場合、値を再計算する必要がなく、
最初の関数呼び出しから保存された値を返すことができることを意味します)。
これにより、同一のpropsに対する再レンダリングを防ぎます。
Yewは内部的にpropsを比較し、propsが変更された場合のみUIが再レンダリングされます。
ワークスペースを使用してコンパイル時間を短縮する
間違いなく、Yewを使用する最大の欠点は、Yewアプリのコンパイルに長い時間がかかることです。
プロジェクトのコンパイルにかかる時間は、html!
マクロに渡されるコードの量に関連しているようです。
これは小規模なプロジェクトではそれほど問題にならない傾向がありますが、大規模なアプリケーションでは、
アプリケーションに加えられた各変更に対してコンパイラが行う必要がある作業量を最小限に抑えるために、
コードを複数のクレートに分割することが理にかなっています。
可能なアプローチの1つは、メインクレートにルーティング/ページ選択を処理させ、
各ページに異なるクレートを作成することです。各ページは異なるコンポーネントか、
Html
を生成する大きな関数になる可能性があります。アプリケーションの異なる部分を含む
クレート間で共有されるコードは、プロジェクトが依存する別のクレートに格納できます。
最良のケースでは、各コンパイルですべてのコードを再ビルドすることから、
メインクレートと1つのページクレートのみを再ビルドすることになります。
最悪の場合、「共通」クレートで何かを編集すると、その共通に共有されるクレートに依存する
すべてのコード(おそらく他のすべて)をコンパイルする元の状態に戻ってしまいます。
メインクレートが重すぎる場合、または深くネストされたページ((例:別のページの上にレンダリングされるページ))で 迅速に反復したい場合は、サンプルクレートを使用してメインページの簡略化された実装を作成し、 作業中のコンポーネントを追加でレンダリングできます。
バイナリサイズを小さくする
- Rust のコードを最適化する
cargo.toml
( release profile を定義 )wasm-opt
を用いて wasm のコードを最適化する
注意: バイナリサイズを小さくするのについてはRust Wasm Bookに詳しく書いてあります。
Cargo.toml
Cargo.toml
で[profile.release]
のセクションに設定を書き込むことでリリースビルドを小さくすることが可能です。
[profile.release]
# バイナリに含むコードを少なくする
panic = 'abort'
# コードベース全体での最適化 ( 良い最適化だがビルドが遅くなる)
codegen-units = 1
# サイズの最適化( よりアグレッシブに )
opt-level = 'z'
# サイズの最適化
# opt-level = 's'
# プログラム全体の分析によるリンク時最適化
lto = true
Nightly Cargo設定
rustとcargoの実験的なナイトリー機能から追加の利点を得ることもできます。
trunk
でナイトリーツールチェーンを使用するには、RUSTUP_TOOLCHAIN="nightly"
環境変数を設定します。
その後、.cargo/config.toml
で不安定なrustc機能を設定できます。
設定を理解するには、unstable featuresのドキュメント、特にbuild-std
と
build-std-features
に関するセクションを参照してください。
[unstable]
# rust-srcコンポーネントが必要です。`rustup +nightly component add rust-src`
build-std = ["std", "panic_abort"]
build-std-features = ["panic_immediate_abort"]
ナイトリーrustコンパイラには、このようなバグが含まれている可能性があり、 時折注意と調整が必要です。これらの実験的オプションは慎重に使用してください。
wasm-opt
更にwasm
のコードのサイズを最適化することができます。
The Rust Wasm Book には Wasm バイナリのサイズを小さくすることについてのセクションがあります: Shrinking .wasm size
wasm-pack
でデフォルトのwasm
のコードをリリースビルド時に最適化するwasm-opt
によって直接wasm
ファイルを最適化する
wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm
yew/examples/にある例を小さなサイズでビルドする
注意: wasm-pack
は Rust と Wasm のコードへの最適化を組み合わせます。wasm-bindgen
はこの例では Rust のサイズ最適化を用いていません。
使用したツール | サイズ |
---|---|
wasm-bindgen | 158KB |
wasm-bindgen + wasm-opt -Os | 116KB |
wasm-pack | 99 KB |