kazuk は null に触れてしまった

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

.NET Framework CLR2 & CLR4 で例外の性能を見てみた


簡単なコードで例外の発行性能を見てみた。
 
private void ThrowCatch( int count )
{
    var exception = new ApplicationException();
    for( int i=0;i<count;i++ ) {
       try {
            throw exception;
        }
        catch( Exception ) {}
    }
}
 
を count を大きめに指定して Stopwatch (System.Diagnostics名前空間のね、手でカチカチしたわけじゃないよ) で経過時間を count で割ってみるって感じ。
(実際には初回にはリソースの読み込みやGITなど処理が絡むので、count=10 で動かしてから、count=1000で呼んでみた。
 
大雑把な計測だが Core2Duo T9600 2.8GHzで .NET 2.0 は x86/x64 共に 30 exception per ms 程度。 .NET 4.0 だと x86 は若干性能下がって 28 exception per ms、x64 は大幅に性能向上が見られて 60超 exception per ms をたたき出した。
 
この値を見ると例外の発生を嫌うチューニングは、あんまり意味がないように見えるし、普通ならしないでもOKと判断できる。
ただし、この値はもう一段別の事象についてもパフォーマンスベクトルがあって、デバッガがアタッチしてる時の事も考慮に入れる必要がある。
 
Visual Studio 2010 のデバッガがアタッチしている状況においては、最速を誇った .NET 4 CLR x64 でも 0,6 exception per ms しか出なかった。
要するに例外についての詳細を知りたいと思ってるような状況においてパフォーマンスが100分の1に劣化するって事、最悪ケースを想定すれば、普通に動かすと1分で再現する事に2時間近く浪費する可能性があるって事ね。
 
Azure では ADO.NET Data Service Client によるTableストレージのアクセスが提供されるんだけど、このTableアクセスは System.Net.HttpWebRequest を使う、HttpWebRequestはRESTの文脈でのデータねーよっていう極当たり前のシチュエーションである 404 Not Foundに対して WebException 例外を放り投げる。GetResponse に TryGetResponse があればいいんだけど無いものなんで仕方が無い。あれまー、残念なことだねぇって感じ。
 
結果として IgnoreResourceNotFoundException なんてプロパティがあって、これをセットしておけば DataServiceContext が try catch して無視してくれるけど、Visual Studio でのデバッグ中はデバッガのアウトプットに 「~の初回例外が発生しました」がずらずらと並び、パフォーマンスは思いっきりスポイルされる。
自分には 「~の初回例外が発生しました」がずらずらと並びの状態で本来のアプリケーションのデバッグをできる精神力が無いんで TryGetResponse を作りました。性能に関しての視点としてリリースバイナリーでの実行だけを考える&デバックなんてしねーよwの普通の人には無駄だったりしますんでレシピだけ。
 
 
からソースをダウンロードする。
HttpWebRequest のGetResponse を切り出して拡張メソッドにする。
メンバフィールド触ってる所を以下のようにもにょる
  アクセサ static readonly Func<HttpWebRequest,フィールドの型> funcHogeField を定義する。
  static コンストラクタで以下のようにアクセサを作る
  Expression paramReq = Expression.Parameter( typeof( HttpWebRequest ), "req" );
  funcHogeField =
     Expression.Lambda(
        Expression.MakeMemberAccess( paramReq, typeof( HttpWebRequest ).GetField( "フィールド名", BindingFlags.Instance | BindingFlags.NonPublic ) )
        paramReq ).Compile();
 んでフィールドを見てるところを funcHogeField( this ) にする。
メソッドを呼んでる所も似たり寄ったりの方法( Expression.Call を使う)でサロゲートする。
System アセンブリの中の internal class を使ってる所は適当なラッパークラスにアクセサを纏めて作る。
 
以上でアクセサのinline化がされなてないって若干のパフォーマンスペナルティはありますが TryGetResponse を作る事ができまする。
 
注) Azure Table の改善のためにはデバッグのためにはそれを呼んでいる Data Service Client も直さなければいけませんが、自分は全部HTTP RESTコールをする方向に行ってしまったんで Data Service Client の修正についてはソンナコトシランです。
広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。