none
複数のプロセス毎のCPU使用率同時取得 RRS feed

  • 質問

  • こんにちは。

    現在プロセス監視のプログラムを作成していまして、CPU使用率をプロセス毎に同時に取得したいのですが、
    PerformanceCounterクラスのNextValueメソッドを使って、一つのプロセスの取得はできているのですが、
    複数のプロセスのCPU使用率をほぼ同じタイミングのデータを取得する方法がわからず詰まっています。

    取得の周期はTimerを使って1秒間隔に設定してあります。(なのでこの1秒の間に処理をし、CPU率を表示させたいのです)

    1つのプロセスにつきPerformanceCounterをひとつ使えば出来る・・・と考えてはいますが、何かもっと良い方法がないかと考えています。
    ちなみに取得するプロセスは不動であり、個数も不動です。40個の時もあれば3個の時も。。という具合なのです。
    取得するプロセスも不動です。

    また、NextValueを配列に入力すれば。。と思ったのですが複数プロセスを取得していてもNextValueを使って取得できるのはやはりひとつだけでした。
    配列での取得は無理のようなので、、、どうすれば同タイミングで取れるのかわかりません。

    もしくはProcessクラスで取得できるカウンタなどで計算し使用率が割り出せる方法があるならばそちらでもかまいません。
    PerformanceCounterクラスのRawValueなどを使えば割り出せるのでしょうか?
    (そもそもRawValueの数値が何の数値なのか全然わかっていませんが・・・生のデータってなに・・)

    英語がわからないので日本語のサイトしか見ておりませんが、検索をしても出てこなかったので書き込ませていただいてます。

    説明下手なのでわかりにくいとは思いますが、なにとぞ汲んで頂き良い方法をご教授いただければと思います。

    .NETは3.0です。
    2009年2月3日 9:30

回答

  • こんばんは、偶然同じような要件のプログラムを組んでいます。
    CPUの使用時間を算出する処理を抜き出しましたので参考になれば幸いです。

    カウンター"% Processor Time"により取得できるのは、直前の1秒の間にCPUを使用した時間(単位:100ナノ秒)になると思われます。
    この値はプロセスを起動している間は常に加算されていくので、取得した値から、その1秒前の値を引くことで、実際に使用した時間が割り出せると思います。
    この値はCPUを使用した時間を表すだけなので、プロセスが複数のコアを使用して動作した場合などは、1秒を超える値を返すことがあります。
    その為、プロセッサのパフォーマンスカウンタを使用してコアの数を割り出し、取得したCPU使用時間をコア数で割ることで、全体の使用率を算出しています。

    コアの数を取得する部分は適当なので、他に最適な方法があるかも知れません。

    #追記:.NET 2.0を使用してのソースです。

    /// <summary>パフォーマンスカウンタ・カテゴリ名(プロセス)</summary> 
    const string CATEGORY_NAME = "Process";  
    /// <summary>パフォーマンスカウンタ・カテゴリ名(プロセッサ)</summary> 
    const string CATEGORY_NAME_PROCESSOR = "Processor";  
    /// <summary>パフォーマンスカウンタ・カウンタ名(プロセッサ使用時間)</summary> 
    const string COUNTER_NAME_PROCESSOR_TIME = "% Processor Time";  
    /// <summary>ナノ秒単位での1秒の値</summary> 
    double NANO_SECOND_TICKS = 10000000;  
    /// <summary>CPUのコア数</summary> 
    double mCoreNumber;  
     
    #region 指定時間間隔内でのデータ格納領域  
    /// <summary>プロセッサ使用時間</summary> 
    double[] mDataContainer_ProcessorPercent;  
    #endregion  
     
    #region 直前の監視タイミングでのデータ格納領域  
    /// <summary>プロセッサ使用時間</summary> 
    double mBefor_ProcesserPercent;  
    #endregion  
     
    #region CPUのコア数取得  
    mCoreNumber = 1;  
    if( PerformanceCounterCategory.Exists( CATEGORY_NAME_PROCESSOR ) )  
    {  
        if( PerformanceCounterCategory.CounterExists( COUNTER_NAME_PROCESSOR_TIME, CATEGORY_NAME_PROCESSOR ) )  
        {  
            for( int i=0; i<32; i++ )  
            {  
                try  
                {  
                    string wkInst = i.ToString();  
                    mWorkCounter_CoreNumber = new PerformanceCounter( CATEGORY_NAME_PROCESSOR, COUNTER_NAME_PROCESSOR_TIME, wkInst, "." );  
                    long buf = mWorkCounter_CoreNumber.RawValue;  
                    mCoreNumber = double.Parse( (i + 1).ToString() );  
                }  
                catch  
                {  
                    break;  
                }  
            }  
        }  
    }  
    #endregion  
     
    #region プロセスのパフォーマンスカウンタ定義  
    // プロセッサ使用時間  
    mProcessTime = new PerformanceCounter(  
        CATEGORY_NAME,  
        COUNTER_NAME_PROCESSOR_TIME,  
        mSystemProcessName,  
        ".");  
    #endregion  
     
    // カレント時間を取得  
    DateTime current = DateTime.Now;  
    // ループ  
    while (true)  
    {  
        // ストップフラグが設定されたらループエンド  
        if (mStop) break;  
     
        // カレント時間の秒とが現在の秒と異なるか?  
        if (DateTime.Now.Second != current.Second)  
        {  
            // カレント時間の更新  
            current = DateTime.Now;  
            // カレント秒をログ出力間隔で割る  
            int div_sec = current.Second % mMonitorInterval;  
     
            // カレント秒がログ出力間隔で割り切れるか?  
            if ( div_sec == 0 )  
            {  
                //------------------------------------------------  
                // 監視間隔が切り替わるタイミングでログ情報出力  
                //------------------------------------------------  
                PutLog( current );      // ログファイルへデータ登録  
                InitWorkArea();         // データ格納クラスの初期化  
            }  
              
            // CPU使用率 CPU使用時間(単位:100ナノ秒) / 1秒(10000000*100ナノ秒)/ CPUのコア数  
            mDataContainer_ProcessorPercent[div_sec] = ( mProcessTime.RawValue - mBefor_ProcesserPercent ) / NANO_SECOND_TICKS  / mCoreNumber;  
     
            // 直前の値として退避  
            mBefor_ProcesserPercent = mProcessTime.RawValue;  
        }  
        Thread.Sleep(10);  
    2009年2月5日 10:17
  • ご報告

    hagechaさんのコードを参考に(大体丸写しなんですが…)、成功しました!
    ありがとうございました。

    複数のプロセスのほぼ同時期のデータが取れました。(精度は計算していないのでわかりませんが、シビアにではないので満足です)
    • 回答としてマーク ysmsdn 2009年2月9日 7:18
    2009年2月9日 7:17

すべての返信

  • # 不動→不定ですよね?

    PerformanceCounterクラスインスタンスを作り置きしておくほかありません。
    毎秒インスタンスを作り直す必要はありませんが、プロセスの増減に合わせてインスタンスも増減させる必要があるでしょう。
    PerformanceCounterCategory.GetInstanceNames()メソッドがちょっとだけ役に立つ程度でしょうか。

    確実に同じタイミングの値が欲しい、もっと効率よくアクセスしたい、となるとPDH.DLLを使うことになりそうですがネイティブDLLなのでC#からの利用は敷居が高いと思います。
    説明はできますが…。

    更に効率を求めた場合、レジストリを使用することになります。一応、Registry.PerformanceDataフィールドも用意されていますが…大きなbyte[]が得られるのでこれを
    自力で解析することになるかと思います。こっちはあまり詳しく調べていません。
    • 編集済み 佐祐理 2009年2月3日 14:26 誤字 orz
    2009年2月3日 12:59
  • 返信ありがとうございます。

    そうですね、不動ではなく不定ですね。間違っていました

    やはり作り置きですか・・・

    佐祐理 の発言:
    PerformanceCounterCategory.GetInstanceNames()メソッドがちょっとだけ役に立つ程度でしょうか。



    このメソッドは見たことある気がします。後ほど調べてみようと思います。
    佐祐理 の発言:
    果実に同じタイミングの値が欲しい、もっと効率よくアクセスしたい、となるとPDH.DLLを使うことになりそうですがネイティブDLLなのでC#からの利用は敷居が高いと思います。
    説明はできますが…。

    更に効率を求めた場合、レジストリを使用することになります。一応、Registry.PerformanceDataフィールドも用意されていますが…大きなbyte[]が得られるのでこれを
    自力で解析することになるかと思います。こっちはあまり詳しく調べていません。

    タイミングに関してはそこまでシビアではなく、間隔である1秒内での算出でよいのでそこは利用しないと思います。
    しかし、レジストリですか。。まだC#を使い始めて2ヶ月なのでそこまで調べたことはなかったですね。。
    実装にはおそらくPerfomanceCounterCategoryを利用するか作り置きインスタンスかな、、と思います。

    2009年2月3日 13:36
  • こんばんは、偶然同じような要件のプログラムを組んでいます。
    CPUの使用時間を算出する処理を抜き出しましたので参考になれば幸いです。

    カウンター"% Processor Time"により取得できるのは、直前の1秒の間にCPUを使用した時間(単位:100ナノ秒)になると思われます。
    この値はプロセスを起動している間は常に加算されていくので、取得した値から、その1秒前の値を引くことで、実際に使用した時間が割り出せると思います。
    この値はCPUを使用した時間を表すだけなので、プロセスが複数のコアを使用して動作した場合などは、1秒を超える値を返すことがあります。
    その為、プロセッサのパフォーマンスカウンタを使用してコアの数を割り出し、取得したCPU使用時間をコア数で割ることで、全体の使用率を算出しています。

    コアの数を取得する部分は適当なので、他に最適な方法があるかも知れません。

    #追記:.NET 2.0を使用してのソースです。

    /// <summary>パフォーマンスカウンタ・カテゴリ名(プロセス)</summary> 
    const string CATEGORY_NAME = "Process";  
    /// <summary>パフォーマンスカウンタ・カテゴリ名(プロセッサ)</summary> 
    const string CATEGORY_NAME_PROCESSOR = "Processor";  
    /// <summary>パフォーマンスカウンタ・カウンタ名(プロセッサ使用時間)</summary> 
    const string COUNTER_NAME_PROCESSOR_TIME = "% Processor Time";  
    /// <summary>ナノ秒単位での1秒の値</summary> 
    double NANO_SECOND_TICKS = 10000000;  
    /// <summary>CPUのコア数</summary> 
    double mCoreNumber;  
     
    #region 指定時間間隔内でのデータ格納領域  
    /// <summary>プロセッサ使用時間</summary> 
    double[] mDataContainer_ProcessorPercent;  
    #endregion  
     
    #region 直前の監視タイミングでのデータ格納領域  
    /// <summary>プロセッサ使用時間</summary> 
    double mBefor_ProcesserPercent;  
    #endregion  
     
    #region CPUのコア数取得  
    mCoreNumber = 1;  
    if( PerformanceCounterCategory.Exists( CATEGORY_NAME_PROCESSOR ) )  
    {  
        if( PerformanceCounterCategory.CounterExists( COUNTER_NAME_PROCESSOR_TIME, CATEGORY_NAME_PROCESSOR ) )  
        {  
            for( int i=0; i<32; i++ )  
            {  
                try  
                {  
                    string wkInst = i.ToString();  
                    mWorkCounter_CoreNumber = new PerformanceCounter( CATEGORY_NAME_PROCESSOR, COUNTER_NAME_PROCESSOR_TIME, wkInst, "." );  
                    long buf = mWorkCounter_CoreNumber.RawValue;  
                    mCoreNumber = double.Parse( (i + 1).ToString() );  
                }  
                catch  
                {  
                    break;  
                }  
            }  
        }  
    }  
    #endregion  
     
    #region プロセスのパフォーマンスカウンタ定義  
    // プロセッサ使用時間  
    mProcessTime = new PerformanceCounter(  
        CATEGORY_NAME,  
        COUNTER_NAME_PROCESSOR_TIME,  
        mSystemProcessName,  
        ".");  
    #endregion  
     
    // カレント時間を取得  
    DateTime current = DateTime.Now;  
    // ループ  
    while (true)  
    {  
        // ストップフラグが設定されたらループエンド  
        if (mStop) break;  
     
        // カレント時間の秒とが現在の秒と異なるか?  
        if (DateTime.Now.Second != current.Second)  
        {  
            // カレント時間の更新  
            current = DateTime.Now;  
            // カレント秒をログ出力間隔で割る  
            int div_sec = current.Second % mMonitorInterval;  
     
            // カレント秒がログ出力間隔で割り切れるか?  
            if ( div_sec == 0 )  
            {  
                //------------------------------------------------  
                // 監視間隔が切り替わるタイミングでログ情報出力  
                //------------------------------------------------  
                PutLog( current );      // ログファイルへデータ登録  
                InitWorkArea();         // データ格納クラスの初期化  
            }  
              
            // CPU使用率 CPU使用時間(単位:100ナノ秒) / 1秒(10000000*100ナノ秒)/ CPUのコア数  
            mDataContainer_ProcessorPercent[div_sec] = ( mProcessTime.RawValue - mBefor_ProcesserPercent ) / NANO_SECOND_TICKS  / mCoreNumber;  
     
            // 直前の値として退避  
            mBefor_ProcesserPercent = mProcessTime.RawValue;  
        }  
        Thread.Sleep(10);  
    2009年2月5日 10:17
  • hagechaさん、ありがとうございます。

    まだ、しっかりと呼んでいないので理解できていませんが、参考にさせていただきます。

    文を見る限りだと、カウンターを取得してきて、自分で計算する方法。。ということでよろしいのでしょうか?
    探してもカウンタを利用し、計算する方法がわからなかったので、これで解決になるといいなと思います。
    早速ためさせていただきます。
    2009年2月9日 1:25
  • ご報告

    hagechaさんのコードを参考に(大体丸写しなんですが…)、成功しました!
    ありがとうございました。

    複数のプロセスのほぼ同時期のデータが取れました。(精度は計算していないのでわかりませんが、シビアにではないので満足です)
    • 回答としてマーク ysmsdn 2009年2月9日 7:18
    2009年2月9日 7:17