Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
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が想 
定した速度を得ることができます。
知るべき 
共通ルール 
(イベント集約とは) 
プロパティA変更→ 
イベント 
パスA パスB 
プロパティA変更→ 
イベント 
プロパティA 
変更通知 
 同一パス内において、1変更に対して1イベントが発生しま 
す。 
 各パスの間において、変更箇所の集約がなされているため、 
次のパスは1回しか動かないようになっています。 
 パスとパスの間は後続処理で非同期です。 
例えば、Width/Heightを変更した際に、 
2回のPropertyChangedイベントが発生します。 
変更の都度イベントが生じます。 
しかし、レイアウトパスでの処理は1回に集約されます。 
このイベント集約を活用することができると、 
本来の速度を獲得できます。 
(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/ 
こちらもご覧ください。

More Related Content

Msを16倍出し抜くwpf開発1回目

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