MVVM と言語のパフォーマンスに関するヒントMVVM and language performance tips

このトピックでは、ソフトウェアの設計パターンとプログラミング言語の選択に関連するいくつかのパフォーマンスの考慮事項について説明します。This topic discusses some performance considerations related to your choice of software design patterns, and programming language.

Model-View-ViewModel (MVVM) パターンThe Model-View-ViewModel (MVVM) pattern

Model-View-ViewModel (MVVM) パターンは、多くの XAML アプリで共通です The Model-View-ViewModel (MVVM) pattern is common in a lot of XAML apps. (MVVM は、Fowler の Model-View-Presenter パターンの説明によく似ていますが、これは XAML に合わせて調整されました)。(MVVM is very similar to Fowler’s description of the Model-View-Presenter pattern, but it is tailored to XAML). MVVM パターンの問題は、このパターンが原因となり、誤ってアプリのレイヤーおよび割り当てが過剰になる可能性があることです。The issue with the MVVM pattern is that it can inadvertently lead to apps that have too many layers and too many allocations. MVVM を使用する利点は次のとおりです。The motivations for MVVM are these.

  • 問題の分離Separation of concerns. 問題を細かく分割することは常に有用であり、MVVM や MVC などのパターンは、アプリ (または 1 つのコントロール) を、実際のビュー、ビューの論理モデル (ビュー モデル)、ビューに依存しないアプリのロジック (モデル) に細かく分割するための方法です。It’s always helpful to divide a problem into smaller pieces, and a pattern like MVVM or MVC is a way to divide an app (or even a single control) into smaller pieces: the actual view, a logical model of the view (view-model), and the view-independent app logic (the model). 具体的には、設計者は 1 つのツールを使ってビューを所有する、開発者は別のツールを使ってモデルを所有する、デザイン インテグレーターは両方のツールを使ってビュー モデルを所有することが、一般的なワークフローです。In particular, it’s a popular workflow to have designers own the view using one tool, developers own the model using another tool, and design integrators own the view-model using both tools.
  • 単体テストUnit testing. ビューに依存せずに、つまりウィンドウの作成や入力の促進などに依存せずに、ビュー モデル (および結果としてモデル) の単体テストを実行できます。You can unit test the view-model (and consequently the model) independent of the view, thereby not relying on creating windows, driving input, and so on. ビューを小さく保つことにより、ウィンドウを作成することなく、アプリの大部分をテストできます。By keeping the view small, you can test a large portion of your app without ever having to create a window.
  • ユーザー エクスペリエンスの変化に対する機敏性Agility to user experience changes. ユーザー エクスペリエンスがエンド ユーザーからのフィードバックに基づいて調整されるため、ビューは最も頻繁に変更され、最新の変更が加えられる傾向があります。The view tends to see the most frequent changes, and the most late changes, as the user experience is tweaked based on end-user feedback. ビューを分離しておくことにより、これらの変更に、より迅速に、アプリへの影響を抑えて対応することができます。By keeping the view separate, these changes can be accommodated more quickly and with less churn to the app.

MVVM パターンには、複数の具体的な定義と、それを実装するためのサード パーティのフレームワークがあります。There are multiple concrete definitions of the MVVM pattern, and 3rd party frameworks that help implement it. ただし、パターンのすべてのバリエーションに厳密に準拠すると、アプリのオーバーヘッドが必要以上に増大します。But strict adherence to any variation of the pattern can lead to apps with a lot more overhead than can be justified.

  • XAML データ バインディング ({Binding} マークアップ拡張) は、1 つにはモデル/ビュー パターンを有効にするために設計されました。XAML data binding (the {Binding} markup extension) was designed in part to enable model/view patterns. {Binding} により、重大なワーキング セットと CPU オーバーヘッドが生じます。But {Binding} brings with it non-trivial working set and CPU overhead. {Binding} を作成すると一連の割り当てが行われるほか、バインディング ターゲットの更新がリフレクションやボックス化の原因となる可能性があります。Creating a {Binding} causes a series of allocations, and updating a binding target can cause reflection and boxing. これらの問題は、ビルド時にバインディングをコンパイルする、{x:Bind} マークアップ拡張によって対処されています。These problems are being addressed with the {x:Bind} markup extension, which compiles the bindings at build time. 推奨事項: {x:Bind} を使用します。Recommendation: use {x:Bind}.
  • MVVM では、一般的な DelegateCommand または RelayCommand ヘルパーなどの ICommand を使用して、ビュー モデルに Button.Click を接続することが一般的です。It’s popular in MVVM to connect Button.Click to the view-model using an ICommand, such as the common DelegateCommand or RelayCommand helpers. ただし、これらのコマンドは、CanExecuteChanged イベント リスナー、ワーキング セットの増加、ページの起動/ナビゲーション時間の増加など、余分な割り当てとなります。Those commands are extra allocations, though, including the CanExecuteChanged event listener, adding to the working set, and adding to the startup/navigation time for the page. 推奨事項: 便利な ICommand インターフェイスを使用する代わりに、分離コードでイベント ハンドラーを配置して、ビュー イベントに添付することを検討してくださいし、それらのイベントが発生したときに、ビュー モデルのコマンドを呼び出します。Recommendation: As an alternative to using the convenient ICommand interface, consider putting event handlers in your code-behind and attaching them to the view events and call a command on your view-model when those events are raised. コマンドが利用できない場合、ボタンを無効にするコードを追加する必要があります。You'll also need to add extra code to disable the Button when the command is unavailable.
  • MVVM では、UI の使用可能なすべての構成でページを作成し、Visibility プロパティを VM でのプロパティにバインドすることによって、ツリーの一部を折りたたむことが一般的です。It’s popular in MVVM to create a Page with all possible configurations of the UI, then collapse parts of the tree by binding the Visibility property to properties in the VM. これにより、起動時間と、場合によってはワーキング セットが不必要に増加します (ツリーの一部は表示されない可能性があるため)。This adds unnecessarily to startup time and possibly to working set (because some parts of the tree may never become visible). 推奨事項: 使用して、 x: 負荷属性またはx: DeferLoadStrategy 属性スタートアップからツリーの不要な部分を遅延させる機能。Recommendations: Use the x:Load attribute or x:DeferLoadStrategy attribute feature to defer unnecessary portions of the tree out of startup. また、ページのさまざまなモードについて個別のユーザー コントロールを作成し、分離コードを使って必要なコントロールのみを読み込みます。Also, create separate user controls for the different modes of the page and use code-behind to keep only the necessary controls loaded.

C++/CX の推奨事項C++/CX recommendations

  • 最新バージョンを使いますUse the latest version. C++/CX コンパイラは継続的にパフォーマンスが向上しています。There are continual performance improvements made to the C++/CX compiler. アプリが最新のツールセットを使ってビルドされていることを確認します。Ensure your app is building using the latest toolset.
  • RTTI (/GR-) を無効にしますDisable RTTI (/GR-). RTTI はコンパイラで既定でオンになっているため、ビルド環境でオフにしない限り、RTTI を使用している可能性があります。RTTI is on by default in the compiler so, unless your build environment switches it off, you’re probably using it. RTTI には大きなオーバーヘッドがあるため、コードが RTTI に大きく依存していない限り、オフにしてください。RTTI has significant overhead, and unless your code has a deep dependency on it, you should turn it off. XAML フレームワークには、コードで RTTI を使用するという要件はありません。The XAML framework has no requirement that your code use RTTI.
  • ppltasks を使いすぎないようにしますAvoid heavy use of ppltasks. ppltasks は、非同期 WinRT API を呼び出すときに非常に便利ですが、重大なコード サイズのオーバーヘッドが生じます。Ppltasks are very convenient when calling async WinRT APIs, but they come with significant code size overhead. C++/CX チームは、パフォーマンスを大幅に向上させる言語機能 (await) の開発を進めています。The C++/CX team is working on a language feature – await – that will provide much better performance. 当面の間、コードのホット パスでの ppltasks の使用を調整してください。In the meantime, balance your use of ppltasks in the hot paths of your code.
  • アプリの "ビジネス ロジック" での C++/CX の使用を避けますAvoid use of C++/CX in the “business logic” of your app. C++/CX は、C++ アプリから WinRT API にアクセスするための便利な方法として設計されています。C++/CX is designed to be a convenient way to access WinRT APIs from C++ apps. これは、オーバーヘッドがあるラッパーを利用します。It makes use of wrappers that have overhead. クラスのビジネス ロジック/モデル内で C++/CX を使用することを避け、コードと WinRT の間の境界で使用するために予約しておきます。You should avoid C++/CX inside the business logic/model of your class, and reserve it for use at the boundaries between your code and WinRT.