Upload
cct-inc
View
4.614
Download
5
Embed Size (px)
DESCRIPTION
コアコンセプトテクノロジー山本礼貴さんの勉強会発表資料です。 WPFのチューニングに関するノウハウで、まさに実践で苦労の末に獲得し、惜しげも無く公開。全4回のうちの第1部です。 今後も楽しみなセッションです。
Citation preview
Microsoftを 16倍出し抜くC# +WPF開発手法第 1回( 1倍までの道)山本礼貴
誰のためか
対象者 C#と .NET Frameworkに対する大まかな知識を持つ人 WPFに対する大まかな知識を持つ人
動機 WPFのプログラムの動作が遅いという懸念や不満
頂上までの道
WPFの根本的問題を解決す
る(16倍~ )
WPFを制御する( 4~ 16倍)
WPFを効率的に使う( 1~ 4倍)
誤った使い方をしないこと(~ 1倍)
アジェンダ 高速化って何? WPFの構造 流れと弱点と対策を知る まとめと次回予告
高速化って何?基礎が最初に必要です。4ページでおさらいします。
一般的手法(今日は一部しか扱いませんが)
正しいデータ構造(コレクションの特性を活かす) 求められるのは変更に対する強さなのか、検索に対する高速性なのか、列挙の速度なのか、省メモリなのか。
効率的なアルゴリズム データ構造に合わせた手法を選ぶ。 同じ処理を無駄に繰り返さないこと(遅延評価、イベント集約等は重要なテクニック)。
チューニング コードのほとんどはループ。ループを速くすれば速くなる。しないループ(省略したループ)が最も早い。
短いコードは速いことが多い( LINQは例外)。 ボトルネックの発見と除去(ベンチマークは徹底的に)。
この色の部分はマイクロソフトが極めて重視してます。
.NET Frameworkでは(この部分も今日のネタではありませんが)
糖衣構文(シンタックスシュガー)の解釈に対する正しい理解
メモリ転送量と演算量を把握すること。 ガーベージコレクションの動きを把握すること。 IL(中間コード)を確認しながらコードを書くこと。 いやそもそも、どんな機械語になるのか理解すること。
WPFでは(構造を知ってないと、一般的手法を役立てる場所を見失います)
4つのパスを理解すること(時間軸の区分) イベント処理とユーザーコード バインディングパス レイアウトパス 描画パス
情報の所在と関連を理解すること(メモリ空間軸の区分) アンマネージ領域
OSに依存するリソース群 マネージ領域
クラスと構造体 ビジュアルツリーとロジカルツリー 依存関係プロパティ
描画命令バッファ(隠ぺいされている) テクスチャ(隠ぺいされている) デバイスのステート管理(隠ぺいされている)
今日のネタ
次回以降のネタ
WPFは遅い?
「悪いパターン」が内在しています パネルの基底クラスに無駄なソートが含まれています。 依存関係プロパティへのアクセスはMicrosoftが主張するほど速くありません。(特に PropertyMetadataの種類に気を付けましょう)
Microsoftが推奨しない使い方は設計通りの速度が出ません。 リッチゆえに遅い部分が存在します
実は、アンチエイリアスを解除するだけで 5倍くらい高速になります。
Freezeするだけで速くなる部分があります。
構造を理解せずに使うと遅いところがあります プロパティ変更から再描画に至るまでにパスを理解していないと、速くすることができません。
見えないコストを理解していないと、それ以前のすべての動作と無関係に遅くなります。
本日の主なネタ
WPFの構造時間軸上の構造を知った上で、やってはいけないことを理解しましょう。すべてはその後。
WPFの骨格1(概念)
ユーザーコード
• 入力やイベントの処理• Model層の変更 (データの更新 )• ViewModel層への変更通知 (表示層への伝達経路 )
データバインディング
• ViewModel層の変更を Viewに伝達 (表示の更新のための設定 )• レイアウトパス用のフラグ設定 (要サイズ測定、要配置・再描画 )
レイアウトパス
• コントロールのサイズ測定• コントロールの配置と描画内容の確定
描画パス
• 最適な方法で描画を実施する( DirectXを内部で使用しています)
MVVMという手法を用いても用いなくても、流れは同じです。
WPFの骨格2(実装)具体的にどこに記述されるのか。理想的にはこれを無駄なく循環させたい。
ユーザーコード
• あらゆるユーザーコード• イベントハンドラ
データバインディング
• XAMLで記述する部分と依存関係プロパティ• OnXXXXXChanged等
レイアウトパス
• MeasureOverride• ArrangeOverride/OnRender
描画パス
• 描画(要はここに速くたどり着きたいだけ)
流れと弱点と対策を知る入力から描画までを淀みなく扱うことで、Microsoftが想定した速度を得ることができます。
知るべき共通ルール(イベント集約とは)
同一パス内において、1変更に対して1イベントが発生します。
各パスの間において、変更箇所の集約がなされているため、次のパスは1回しか動かないようになっています。
パスとパスの間は後続処理で非同期です。例えば、Width/Heightを変更した際に、2回の PropertyChangedイベントが発生します。変更の都度イベントが生じます。しかし、レイアウトパスでの処理は 1回に集約されます。
このイベント集約を活用することができると、本来の速度を獲得できます。
パス A パス B
プロパティ A変更→イベント
プロパティ A変更→イベント
プロパティ A変更通知(1回 )
ユーザーコード×
バインディング
依存関係プロパティを何回変更してもバインディング処理は1回です。
依存関係プロパティに書く処理自体が重たいので要注意(最小限の書き込み回数を心がけてください)
Opacity(透過率)の設定はコントロールに対して極力行わないようにしましょう。
可能な限り Freezeしましょう。
バインディングはパスが違うために非同期なので、要注意です。
ユーザーコード
• 入力やイベントの処理
• Model層の変更• ViewModel層への変更通知
データバインディング
• ViewModel層の変更を Viewに伝達
• レイアウトパス用のフラグ設定(要サイズ測定、要配置・描画)
バインディング×
レイアウトパス
バインディング中に、依存関係プロパティは変更されます。
無限ループに陥らない範囲でのプロパティ変更は自由です(ただし、遅い)。
描画命令の蓄積( OnRender)までがレイアウト処理です。
レイアウトパスの中で、依存関係プロパティの変更をすると、バインディングとレイアウトパスはやり直しです(初心者がはまる罠)。
データバインディング
• ViewModel層の変更を Viewに伝達
• レイアウトパス用のフラグ設定(要サイズ測定、要配置・描画)
レイアウトパス
• コントロールのサイズ測定
• コントロールの配置と描画内容の確定
レイアウトパス×
描画パス
OnRenderが呼ばれた順序で描画されるわけではありません。
通常は Panel.Zindexの順序で描画されます。
内部的にはビジュアルツリーの親子リレーションシップが構築された順序で描画されます。
描画パスにはユーザーコードの介在余地はありませんが、 OnRenderの書き方と Opacityの設定が悪いと遅くなります。
レイアウトパス
• コントロールのサイズ測定
• コントロールの配置と描画内容の確定
描画パス
• 最適な方法で描画を実施する( DirectXを内部で使用しています)
WPFの急所3選
全パネル内の最凶ボトルネックは Panel.ZIndexのソート 子要素が1つでも変わると ZIndexを工夫もなくソートしています。 Childrenの着脱を最小限にする必要があります。
Canvas,Grid,StackPanel等、すべてのパネルは Panelを継承し、 Panelの機能をすべて使っています。
レイアウトパス(Measure/Arrange/OnRender)で依存関係プロパティ変更をすること
再バインディングが実施されて、レイアウトパスが何度も走ります。
描画パスにおいてコントロールの透過率変更は極力回避。せめてブラシの色の透過率を変えましょう。
透過率を実現するために、一時テクスチャを生成して描画することがあります。死ぬほど重いです。
そもそも透過率変更は Direct3D内部の Flush処理を呼ぶことが多いので、描画の終了待ちが挟まれます。
次点: Freezeしていない描画要素 イベント乱れ打ち状態になる場合があります。
まとめと次回予告
まとめ(まず1倍の速度を得ます) 流れを乱さなければ設計通りの速度を得ることができます。
実際に遅いと言われる例は、前述のパスの振る舞いを無視しているケースが圧倒的に多い。
急所に極力触れないようにすることが大切。
次回以降予告 ビジュアルツリーを理解しましょう。ビジュアルツリーを効率的に再描画するアルゴリズムと実装方法。
Panel.ZIndexをどうやって解決するか。
MSDNにすら詳述されない(でもリファレンスコードを読めばはっきりと読み取れる)最速のパネルの作り方とは・・・。MVVMでも旧方式でも効いてくる方法があります。
ご清聴ありがとうございました。http://proprogrammer.hatenadiary.jp/
こちらもご覧ください。