20
Microsoft を 16 ををををを C# WPF をををを を1を1 ををををををををを

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

  • Upload
    cct-inc

  • View
    4.614

  • Download
    5

Embed Size (px)

DESCRIPTION

コアコンセプトテクノロジー山本礼貴さんの勉強会発表資料です。 WPFのチューニングに関するノウハウで、まさに実践で苦労の末に獲得し、惜しげも無く公開。全4回のうちの第1部です。 今後も楽しみなセッションです。

Citation preview

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

Microsoftを 16倍出し抜くC# +WPF開発手法第 1回( 1倍までの道)山本礼貴

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

誰のためか

対象者 C#と .NET Frameworkに対する大まかな知識を持つ人 WPFに対する大まかな知識を持つ人

動機 WPFのプログラムの動作が遅いという懸念や不満

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

頂上までの道

WPFの根本的問題を解決す

る(16倍~ )

WPFを制御する( 4~ 16倍)

WPFを効率的に使う( 1~ 4倍)

誤った使い方をしないこと(~ 1倍)

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

アジェンダ 高速化って何? WPFの構造 流れと弱点と対策を知る まとめと次回予告

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

高速化って何?基礎が最初に必要です。4ページでおさらいします。

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

一般的手法(今日は一部しか扱いませんが)

正しいデータ構造(コレクションの特性を活かす) 求められるのは変更に対する強さなのか、検索に対する高速性なのか、列挙の速度なのか、省メモリなのか。

効率的なアルゴリズム データ構造に合わせた手法を選ぶ。 同じ処理を無駄に繰り返さないこと(遅延評価、イベント集約等は重要なテクニック)。

チューニング コードのほとんどはループ。ループを速くすれば速くなる。しないループ(省略したループ)が最も早い。

短いコードは速いことが多い( LINQは例外)。 ボトルネックの発見と除去(ベンチマークは徹底的に)。

この色の部分はマイクロソフトが極めて重視してます。

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

.NET Frameworkでは(この部分も今日のネタではありませんが)

糖衣構文(シンタックスシュガー)の解釈に対する正しい理解

メモリ転送量と演算量を把握すること。 ガーベージコレクションの動きを把握すること。 IL(中間コード)を確認しながらコードを書くこと。 いやそもそも、どんな機械語になるのか理解すること。

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

WPFでは(構造を知ってないと、一般的手法を役立てる場所を見失います)

4つのパスを理解すること(時間軸の区分) イベント処理とユーザーコード バインディングパス レイアウトパス 描画パス

情報の所在と関連を理解すること(メモリ空間軸の区分) アンマネージ領域

OSに依存するリソース群 マネージ領域

クラスと構造体 ビジュアルツリーとロジカルツリー 依存関係プロパティ

描画命令バッファ(隠ぺいされている) テクスチャ(隠ぺいされている) デバイスのステート管理(隠ぺいされている)

今日のネタ

次回以降のネタ

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

WPFは遅い?

「悪いパターン」が内在しています パネルの基底クラスに無駄なソートが含まれています。 依存関係プロパティへのアクセスはMicrosoftが主張するほど速くありません。(特に PropertyMetadataの種類に気を付けましょう)

Microsoftが推奨しない使い方は設計通りの速度が出ません。 リッチゆえに遅い部分が存在します

実は、アンチエイリアスを解除するだけで 5倍くらい高速になります。

Freezeするだけで速くなる部分があります。

構造を理解せずに使うと遅いところがあります プロパティ変更から再描画に至るまでにパスを理解していないと、速くすることができません。

見えないコストを理解していないと、それ以前のすべての動作と無関係に遅くなります。

本日の主なネタ

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

WPFの構造時間軸上の構造を知った上で、やってはいけないことを理解しましょう。すべてはその後。

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

WPFの骨格1(概念)

ユーザーコード

• 入力やイベントの処理• Model層の変更 (データの更新 )• ViewModel層への変更通知 (表示層への伝達経路 )

データバインディング

• ViewModel層の変更を Viewに伝達 (表示の更新のための設定 )• レイアウトパス用のフラグ設定 (要サイズ測定、要配置・再描画 )

レイアウトパス

• コントロールのサイズ測定• コントロールの配置と描画内容の確定

描画パス

• 最適な方法で描画を実施する( DirectXを内部で使用しています)

MVVMという手法を用いても用いなくても、流れは同じです。

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

WPFの骨格2(実装)具体的にどこに記述されるのか。理想的にはこれを無駄なく循環させたい。

ユーザーコード

• あらゆるユーザーコード• イベントハンドラ

データバインディング

• XAMLで記述する部分と依存関係プロパティ• OnXXXXXChanged等

レイアウトパス

• MeasureOverride• ArrangeOverride/OnRender

描画パス

• 描画(要はここに速くたどり着きたいだけ)

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

流れと弱点と対策を知る入力から描画までを淀みなく扱うことで、Microsoftが想定した速度を得ることができます。

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

知るべき共通ルール(イベント集約とは)

同一パス内において、1変更に対して1イベントが発生します。

各パスの間において、変更箇所の集約がなされているため、次のパスは1回しか動かないようになっています。

パスとパスの間は後続処理で非同期です。例えば、Width/Heightを変更した際に、2回の PropertyChangedイベントが発生します。変更の都度イベントが生じます。しかし、レイアウトパスでの処理は 1回に集約されます。

このイベント集約を活用することができると、本来の速度を獲得できます。

パス A パス B

プロパティ A変更→イベント

プロパティ A変更→イベント

プロパティ A変更通知(1回 )

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

ユーザーコード×

バインディング

依存関係プロパティを何回変更してもバインディング処理は1回です。

依存関係プロパティに書く処理自体が重たいので要注意(最小限の書き込み回数を心がけてください)

Opacity(透過率)の設定はコントロールに対して極力行わないようにしましょう。

可能な限り Freezeしましょう。

バインディングはパスが違うために非同期なので、要注意です。

ユーザーコード

• 入力やイベントの処理

• Model層の変更• ViewModel層への変更通知

データバインディング

• ViewModel層の変更を Viewに伝達

• レイアウトパス用のフラグ設定(要サイズ測定、要配置・描画)

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

バインディング×

レイアウトパス

バインディング中に、依存関係プロパティは変更されます。

無限ループに陥らない範囲でのプロパティ変更は自由です(ただし、遅い)。

描画命令の蓄積( OnRender)までがレイアウト処理です。

レイアウトパスの中で、依存関係プロパティの変更をすると、バインディングとレイアウトパスはやり直しです(初心者がはまる罠)。

データバインディング

• ViewModel層の変更を Viewに伝達

• レイアウトパス用のフラグ設定(要サイズ測定、要配置・描画)

レイアウトパス

• コントロールのサイズ測定

• コントロールの配置と描画内容の確定

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

レイアウトパス×

描画パス

OnRenderが呼ばれた順序で描画されるわけではありません。

通常は Panel.Zindexの順序で描画されます。

内部的にはビジュアルツリーの親子リレーションシップが構築された順序で描画されます。

描画パスにはユーザーコードの介在余地はありませんが、 OnRenderの書き方と Opacityの設定が悪いと遅くなります。

レイアウトパス

• コントロールのサイズ測定

• コントロールの配置と描画内容の確定

描画パス

• 最適な方法で描画を実施する( DirectXを内部で使用しています)

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

WPFの急所3選

全パネル内の最凶ボトルネックは Panel.ZIndexのソート 子要素が1つでも変わると ZIndexを工夫もなくソートしています。 Childrenの着脱を最小限にする必要があります。

Canvas,Grid,StackPanel等、すべてのパネルは Panelを継承し、 Panelの機能をすべて使っています。

レイアウトパス(Measure/Arrange/OnRender)で依存関係プロパティ変更をすること

再バインディングが実施されて、レイアウトパスが何度も走ります。

描画パスにおいてコントロールの透過率変更は極力回避。せめてブラシの色の透過率を変えましょう。

透過率を実現するために、一時テクスチャを生成して描画することがあります。死ぬほど重いです。

そもそも透過率変更は Direct3D内部の Flush処理を呼ぶことが多いので、描画の終了待ちが挟まれます。

次点: Freezeしていない描画要素 イベント乱れ打ち状態になる場合があります。

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

まとめと次回予告

まとめ(まず1倍の速度を得ます) 流れを乱さなければ設計通りの速度を得ることができます。

実際に遅いと言われる例は、前述のパスの振る舞いを無視しているケースが圧倒的に多い。

急所に極力触れないようにすることが大切。

次回以降予告 ビジュアルツリーを理解しましょう。ビジュアルツリーを効率的に再描画するアルゴリズムと実装方法。

Panel.ZIndexをどうやって解決するか。

MSDNにすら詳述されない(でもリファレンスコードを読めばはっきりと読み取れる)最速のパネルの作り方とは・・・。MVVMでも旧方式でも効いてくる方法があります。

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

ご清聴ありがとうございました。http://proprogrammer.hatenadiary.jp/

こちらもご覧ください。