2014年10月18日土曜日

[.NET] 独自のパフォーマンスカウンターを作成する

[動機づけ&概要]
システムの性能を計測したい場合に、
各処理の開始/終了時刻をログファイルへ出力して開始前後で差分を取る方法が良く利用される。
が、ランニング中のとある時刻にパフォーマンスの劣化が確認された場合、これを解析しようとしても、各処理で時間軸の相関を取るのが煩わしく(別途分析ツールなんて作りたくない!)、これを解決するための手段としてパフォーマンスカウンターが利用出来るのでは?と考え、まずは独自のカウンターを作成するサンプルを書いてみた(ほぼMSDNからの引用だけど)。

[展望]
DBアクセス、外部との通信時間など性能劣化を引き起こす要因に対して、これらを計測するためのカウンターを用意し、Perfmonでそれぞれの相関を確認出来れば、分析は捗りそう。
標準で用意されているカウンター(メモリやディスク, CLR関連)も併用する事が出来る。

[参考]
<PerformanceCounter クラス>
http://msdn.microsoft.com/ja-jp/library/system.diagnostics.performancecounter(v=vs.110).aspx

[サンプルコード]
namespace Perfmon
{
    public class Manager
    {
        public static PerformanceCounter AverageBase;
        public static PerformanceCounter Count;
        public static PerformanceCounter AverageTime;

        public static void Setup()
        {
            if (PerformanceCounterCategory.Exists("MyCategory"))
            {
                PerformanceCounterCategory.Delete("MyCategory");
            }

            var creationDataCollection = new CounterCreationDataCollection()
            {
                new CounterCreationData("Count", "累計試行回数", PerformanceCounterType.NumberOfItems32),
                new CounterCreationData("AverageTime", "平均処理時間", PerformanceCounterType.AverageTimer32),
                new CounterCreationData("AverageBase", "ベースカウンター", PerformanceCounterType.AverageBase),
            };
            PerformanceCounterCategory.Create("MyCategory", "まいかてごりー", PerformanceCounterCategoryType.SingleInstance, creationDataCollection);

            Count = new PerformanceCounter(
                "MyCategory",
                "Count",
                false);
            Count.RawValue = 0;
            AverageTime = new PerformanceCounter(
                "MyCategory",
                "AverageTime",
                false);
            AverageTime.RawValue = 0;
            AverageBase = new PerformanceCounter(
                "MyCategory",
                "AverageBase",
                false);
            AverageBase.RawValue = 0;
        }

    }
}
namespace PerfmonTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Perfmon.Manager.Setup();

            var randam = new Random(DateTime.Now.Millisecond);
            while(true)
            {
                Perfmon.Manager.Count.Increment();

                Perfmon.Manager.AverageTime.RawValue = Stopwatch.GetTimestamp();
                //. 処理単位ごとにベースカウンターをインクリメントする。
                //. 2回分の処理として扱うなら、2回分インクリメントする。
                Perfmon.Manager.AverageBase.Increment();
                //. Perfmon.Manager.AverageBase.IncrementBy(2);

                //. 何らかの処理
                System.Threading.Thread.Sleep(1000 + 100*randam.Next(-9,9));
            }
        }
    }
}
[動作確認]
Perfmon.exeを開いてカウンターを見てみると、
カテゴリ「MyCategory」が増え、その中にカウンター「AverageTime」「Count」が追加されているのが確認出来る。ベースカウンター「AverageBase」は表示されない模様。また、何故かインスタンス(対象プロセス)が出てこない。。これではプロセス単位で計測出来ないため、要確認。




















実際に計ってみる(PrivateByteも追加してみた)。





















[留意事項]
・PerformanceCounterCategory.Createの実行には管理者権限が必要。
 (ex)「app.manifest」
  <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />を記載

・CounterCreationDataCollectionへCounterCreationDataを追加する時の追加順について。
 ベースカウンターはこれを利用するカウンターの直後に位置している必要がある。
 (ex)NGとなるケース
            var creationDataCollection = new CounterCreationDataCollection()
            {
                new CounterCreationData("AverageBase", "ベースカウンター", PerformanceCounterType.AverageBase),
                new CounterCreationData("AverageTime", "平均処理時間", PerformanceCounterType.AverageTimer32)
           };
  ⇒上記のようにベースカウンタを前に移動させるとInvalidOperationExceptionがThrowされる。



0 件のコメント:

コメントを投稿