kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

カテゴリーアーカイブ: VS2012

Visual Studio 2012 の新しいDSL Tools


Visual Studio 2012 の新しい DSL Tools を色々試したので、メモなど。

DSL Tools とは

DSL(Domain Specific Language) ドメイン特化言語を扱う為の Visual Studio 拡張を作る為の物。 DSL Tools はVisual Studio 2005 からできたもので、VS2010移行では Visual Studio Visualization & Modeling SDK に統合されている。

最新の Visual Studio Visualization & Modeling SDK のダウンロード URL は以下のとおり。

http://www.microsoft.com/en-us/download/details.aspx?id=30680

Visual Studio 2010 での Visualization & Modeling SDK との違い

ぶっちゃけあまり変わってない。

WPF デザイナと、WinForms デザイナの選択ができるのも一緒、いわゆるダイアグラムデザイナコンポーネントの類( Box を線で結ぶような物 )は GDI+ でしか提供されていないのも変わらない。 Win Forms と WPF は Alternative ではなく相互にコンポーネントの乗せあいをしながら補完しあう物という筋は Visual Studio 内で当たり前のように行われていてその通りの考え方で Visualization & Modeling SDK も実装されている。

WPF デザイナを選んだ時に生成される UI が単なる表形式 View でぶっちゃけショボイのも変わってない。

そして、待望の WinForms Designer の生成オプションが追加(だと思う、VS2010 版DslToolsの比較対象機をアップグレードしちゃったのでもう解らない)、 Visual Studio のデザイナコンポーネントに依存しない Pure な Win Froms なデザインビューを作れるようになりました。生成物は WPF 版デザイナと変わらない単なるグリッドビューだったりして、コレ追加した意味わかんない系。

image

纏めると、MinimalLanguage が旧来からの GDI+ でのダイアグラムデザイナを実装する物。

Minimal WinForms Designer は WPF 版デザイナ(単なる表形式ビュー)と全く同一構造の Windows Forms 版、 Minimal WPF Desinger は VS2010 からある WPF 版(単なる表形式ビュー)

WinForms 版は BindingSource をうまく使う事でいわゆるMVVM的なデータバインディング一辺倒なつくりになってますね。

imageimage

WPF も WinForms もユーザーコントロールをデザイナサーフェースにできるという事は一緒という事で、その手段でのAPI差異の吸収以外には DslPackage に違いはありません。Dsl 側は全く一緒じゃないかな。

Windows Workflow のデザイナがリホスティング可能だったりしてデザイナコンポーネントをVS依存せずにとかできるんじゃない?とか思ってたりしたんですけど、そこにまつわる変更は無いもよう。 WPF/WinForms の何にもないUIから Windows Workflowのチームがシコシコと再発名したか VS のデザイナコンポーネントから相当量のコードを分岐したってことですか。

良くなったこと

http://msdn.microsoft.com/ja-jp/library/bb126259.aspx 機械翻訳だけど、ちゃんとメニューがつながってる!画期的!(以前は言語を英語に切り替えないといけなかったはず)

DSL Details の表示が壊れていくのが無くなった気がする。

ModelBus 接続が可能になったようだ。

まとまらない

まとめようにもまとまらない。メモだから。

広告

Visual Studio 2012 でのパフォーマンスプロファイリング


どーも、どーも、どーもです。

Visual Studio Advent Calender 2012 が中々枠が埋まり切らないという悲鳴が上がってたので、参加しましたです。

というわけで、表題通りパフォーマンスプロファイリングについて書かせてもらいます。

Visual Studio でのパフォーマンスプロファイリング機能は Visual Studio 2010 までは Premium 以上で有効、Visual Studio 2012 では Professional 以降で有効という事で、Visual Studio の上位エディション機能がやっと普通のエディションで使えるようになったという事になります。

というわけで、プロファイリングによってなんかしらプログラムのボトルネックが検出できるかという事で緩く募集したら msgpack-cli をプロファイリングしてみてほしいという事で試してみました。

https://twitter.com/kazuk/status/282008544600023040

Visual Studio でのパフォーマンスプロファイリングの開始方法

テストプロジェクトがあれば、テストの実行中にプロファイリングを行う事ができます。「テスト」メニュー「テストのプロファイル」ただし、プロファイリングがストアアプリのプロファイリングにまだ対応していないので msgpack-cli に対して即座に試す事はできませんでした。実際問題としてパフォーマンス問題を抱えたところをテストとして作りこむとCIサーバとかでスローテストになって邪魔になるのでお勧めしませんです。

image

コンソールアプリケーションを含む一般的な実行可能なものであればプロファイルは可能ですので、今回はテストの中で[Explicit]で除外されてる物から TestStringMedium を選び、コンソールアプリケーションに同等物をこさえてみました。

現物ソースはこちら。

https://gist.github.com/4357661

最初オリジナルのテストを数1000回実行するようにしたのですが、結果的にテスト側での StringBuilder での文字列構築がボトルネックとなったので、一回構築した文字列に1万回のシリアライズ、デシリアライズを行う様にしてみました。

このコンソールアプリケーションをスタートアッププロジェクトに指定して、パフォーマンスプロファイラを起動します。

「分析」メニューから「パフォーマンスウィザードの起動」でプロファイリング方法などの設定を行いながらプロファイリングを開始します。

imageimage

初期状態として「CPUサンプリング」がプロファイリングの方法として選択されています。このプロファイリングでは一定時間毎に実行スレッドを止めスタックトレースを取って何を実行しているかを記録する方法が使われます。なので相当に実行時間のかかる処理以外はプロファイリングできませんし、ほとんど実行時間を使わない部分については結果に現れません。

性能のボトルネックがどこなのかを把握するのに最も適したプロファイリング方法になります。次にプロファイル対象プロジェクトの選択になります。自分で用意したプロジェクトを指定して次へ進みます。

imageimage

完了時にプロファイルを開始するを指定していればパフォーマンステストが開始されます。

image

相当しつこく実行するようにしているのでプログラムは中々終わりません、定常実行状態になって1分ちょいでも実行すれば十分なサンプルが集まりますのでコンソールアプリケーションの実行ウィンドウを閉じて終了させます。

分析していますの表示後に以下のような分析結果が表示されます。

image

最初に表示されているのは CPU 使用率のグラフで、このテストの間 1 Core をほぼ占有してプログラムは動作していた事になります。次にホットパスとしてサンプリング結果が示していた関数が最も多く実行していた関数が表示されています。

92.4% のCPU時間が TestStringMedium で消費されているのでプロファイリングとしてはターゲットをちゃんと実行している事になります。TestString は実際に Pack/Unpack を行っていますが、実際にはPackの方が 59% を占有、Unpackは8%弱しか使っていない事が解ります。

imageimage

デシリアライズの性能改善が可能であるかという話なので、少ない側ですがUnpack に降りていきましょう。

imageimageimageimageimageimageimageimage

単純に呼び出し先が mscorlib とかマングリングされたネイティブメソッドになるまで降りた結果としては上記の流れになります。

8メソッドにわたって降りて行きましたが最終的には new byte[] と Stream.Read に処理時間が使われている事がこれでわかります。ここの時間消費を削るにはどんな方法があるでしょうか。 new byte[] を削るにはバッファ管理を入れて同一の長さのバッファを再利用する等の方策もあったりしますけど、上位の構造を大きくいじらない限りには無理でしょう。Stream.Read の処理時間を削る方法は多分無いと思います。メモリストリームからの読み出しですから単純にバイト列のコピーに時間を取られているはずです。

実際のところとして new byte[] は 0初期化を含んでいます、0で初期化したうえで Stream.Read で内容をすべて上書きするのはちょっと無駄と言えば無駄です、しかしこれをプログラミング的に避ける事はできません。

さて Stream からバイト列を読む方法にはもう一つあり BinaryReader.ReadBytes があります。これを使った場合どうなるでしょう。

image

結果的には若干のコスト増になってしまいました、BinaryReader を new しているところで 0.3%の増加が最も大きく響いています、before / after で見ると new byte[] と Stream.Read  で 8% だった物が BinaryReader.ReadBytes の呼び出しで 8.1% と若干の増加も見られます。失敗と見ていいでしょう。 mscorlib に対しては NGen で結構厳しく最適化かけているという噂もなかったりしますけど、 BinaryReader.ReadBytes にはその効果は及んでいないと見て良いでしょう。

imageimage

実際 BinaryReader.ReadBytes の IL を眺めてみても Stream.Read で上書きされる事が明らかな配列の初期化を省略するような確保方法は行われていませんから順当な結果です。

それでも自分は変質的なスピード狂だったりする

Stream.Read には改善の余地はなくとも、バッファの確保と0初期化を避けるためにできる事はいくらかあったりします。

というわけでバッファの管理と再利用を実装してバッファを使いまわしてあげましょう。

人様のライブラリに勝手に API を追加しちゃいます。 MessagePackObject に Release メソッドを追加

        public void Release()
        {
            if (this._handleOrTypeCode is MessagePackString)
            {
                var packString = this._handleOrTypeCode as MessagePackString;
                packString.Release();
            }

        }

MessagePackString に Release と Alloc を追加してバッファを再利用できるようにします。

        static Dictionary<int, Stack<byte[]>> _buffers = new Dictionary<int, Stack<byte[]>>();

        public void Release()
        {
            Stack<byte[]> bufferStack;
            if (_buffers.TryGetValue(this._encoded.Length,out bufferStack))
            {
                bufferStack.Push(this._encoded);
            }
            else
            {
                _buffers.Add(this._encoded.Length, new Stack<byte[]>( new[] { this._encoded }));
            }
        }

        public static byte[] Allocate(int length)
        {
            Stack<byte[]> bufferStack;
            return _buffers.TryGetValue(length, out bufferStack) 
                ? bufferStack.Pop() 
                : new byte[length];
        }

テストコード側から Release を呼ぶようにして、バッファの再利用を促すのと同時に読み込みコードで Allocate によってバッファを確保するようにしてプロファイルした結果は以下。

image

単純比較はできないかもしれませんが、3割強の性能改善が実現できました。(まぁ、バッファの管理系はシングルスレッドだから動いてるレベルの実装なのでマルチスレッドに対応するともっと性能は下がってしまうかもしれません。)

まとめ

無理やりチューニングすれば早くできるところは多少なりともあったりしますが API 変わってしまうとそれを使っている所は作り直しになってしまいます。だから早いうちから API の利用者が増えないうちに、常にプロファイリングを取り続けてチューニングしましょう。パフォーマンス問題がAPI変更に波及すると出来上がってからの性能改善、チューニングはまず無理です。

まぁ、アプリケーションの枝葉末端はアプリケーションへの性能影響は殆ど無いのも事実ですので、アプリケーションへの性能影響の無い末端はチューニングする必要もありませんので、このコードがシステムの性能を支えている物なのかどうなのか、そこを把握するためにもプロファイリングは行ってくださいね。

今回は CPU サンプリングによる CPU 時間を元にチューニングをしましたが、他のプロファイリング方法も重要といえば重要です。システムがメモリ枯渇に陥っているなど GC が頻繁に活動して性能が下がっている等状況に応じて使い分けていきましょう。

Azure and Visual Studio 2012 : サーバーエクスプローラーでつなぐ


新ポータルで表示まわりが変わりーの、Visual Studio 2012 で設定まわりが変わりーのでわけわかめなのでメモ

Visual Studio のサーバーエクスプローラーで Azure Storage を見るための設定など。

VS側のサーバーエクスプローラーでAdd New Storage Account でアカウントを登録するダイアログを出す。

image

image

ポータル上の表示は Account name はストレージの表示名(作成時の URL の欄で入力したもの)、Account key は Access Key を入れる。

imageimage

まとめ

用語が揺れ揺れすぎてもう… Azure でアカウントと言ったら請求関連のあれのどっかかなーとか、そういう類推は効きません。ってことが必要知識です!