C# Tips | ログ・例外・診断:CPU使用率取得

C# C#
スポンサーリンク

はじめに:CPU使用率取得は「どれだけ“頭フル回転”しているか」を数字で知るための道具

「サーバーが重い」「このバッチ、CPU食い過ぎじゃない?」 こういう会話、業務システムだとよく出てきますよね。

でも、感覚だけで「重そう」と言っていても、 どの処理が本当に CPU を使い倒しているのかは分かりません。

そこで必要になるのが CPU使用率取得、 つまり「このプロセスが、OS 全体の CPU のうち何%を使っているか」を数字で知ることです。

ここでは、初心者向けに

  • 「CPU使用率ってそもそも何を意味しているか」
  • C# での基本的な取得方法(Process と時間差計測)
  • 簡単な CPU使用率ユーティリティ
  • ログとの組み合わせ方と、実務での使いどころ

を、例題付きでかみ砕いて説明します。

CPU使用率のイメージをつかむ

「1秒間のうち、どれくらいCPUを占有していたか」という感覚

CPU使用率は、ざっくり言うと

「ある時間のあいだに、 このプロセスが CPU をどれくらい使っていたかの割合」

です。

例えば、1 秒間のうち 0.5 秒分だけ CPU を使っていたなら、 そのプロセスの CPU 使用率は 50% というイメージになります。

重要なのは、「CPU使用率は“瞬間の値”ではなく、“ある期間に対する割合”」だということです。 だから、必ず「時間を区切って測る」必要があります。

Process で「CPU時間」を取り、時間差から使用率を計算する

TotalProcessorTime を使った基本的な考え方

C# では、System.Diagnostics.Process を使うと、 「このプロセスが今までに使った CPU 時間」を取得できます。

using System;
using System.Diagnostics;

public static class CpuUsageSampler
{
    public static double GetCpuUsagePercent(TimeSpan interval)
    {
        using var process = Process.GetCurrentProcess();

        var startCpuTime = process.TotalProcessorTime;
        var startTime = DateTime.UtcNow;

        // 一定時間待つ
        System.Threading.Thread.Sleep(interval);

        process.Refresh(); // 情報を更新
        var endCpuTime = process.TotalProcessorTime;
        var endTime = DateTime.UtcNow;

        var cpuTimeDelta = (endCpuTime - startCpuTime).TotalMilliseconds;
        var timeDelta = (endTime - startTime).TotalMilliseconds;

        if (timeDelta <= 0)
        {
            return 0;
        }

        // 単一CPU前提の使用率(マルチコアの場合は後述)
        var usage = cpuTimeDelta / timeDelta * 100.0;
        return usage;
    }
}
C#

使い方の例です。

var usage = CpuUsageSampler.GetCpuUsagePercent(TimeSpan.FromSeconds(1));
Console.WriteLine($"CPU使用率(約1秒間の平均): {usage:F1}%");
C#

ここでやっていることはシンプルです。

  • 計測開始時点の「プロセスの累積CPU時間」を取る
  • 一定時間待つ
  • 計測終了時点の「累積CPU時間」を取る
  • その差分を「経過時間」で割って、割合(%)にする

重要なのは、「CPU使用率は“差分”でしか分からない」ということです。 「今までに合計何秒使ったか」だけでは、割合は出せません。

マルチコア環境での考え方

コア数を意識すると「100%を超える」こともある

上の計算式は「単一CPU前提」の使用率です。 しかし、今どきのサーバーはほぼマルチコアなので、 「CPU使用率が 200%」のような値が出ることもあります。

例えば、4 コアの環境で、

  • 1 秒間に 2 秒分の CPU時間を使った(2 コア分をフルで使った)

という場合、 単純計算では 200% になります。

もし「OS 全体に対する割合(タスクマネージャーのイメージ)」に近づけたいなら、 コア数で割るという考え方もあります。

public static double GetCpuUsagePercentNormalized(TimeSpan interval)
{
    using var process = Process.GetCurrentProcess();

    var startCpuTime = process.TotalProcessorTime;
    var startTime = DateTime.UtcNow;

    System.Threading.Thread.Sleep(interval);

    process.Refresh();
    var endCpuTime = process.TotalProcessorTime;
    var endTime = DateTime.UtcNow;

    var cpuTimeDelta = (endCpuTime - startCpuTime).TotalMilliseconds;
    var timeDelta = (endTime - startTime).TotalMilliseconds;

    if (timeDelta <= 0)
    {
        return 0;
    }

    var rawUsage = cpuTimeDelta / timeDelta * 100.0;

    int processorCount = Environment.ProcessorCount;
    var normalized = rawUsage / processorCount;

    return normalized;
}
C#

これで、「全コア合計の使用率」を「1 台の CPU 全体に対する%」として見ることができます。

ここでの重要ポイントは、

「CPU使用率は“何に対する割合か”を意識する」 「マルチコアでは、100%を超える値が出ても不思議ではない」

ということです。

ログと組み合わせた CPU使用率ユーティリティ

「どの処理の前後でCPUが跳ねているか」を後から追えるようにする

業務システムでは、CPU使用率を一度コンソールに出して終わり、ではもったいないです。 ログに残しておくと、「どの処理がCPUを食っているか」を後から分析できます。

ILogger と組み合わせたユーティリティの例です。

using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Extensions.Logging;

public static class CpuLogger
{
    public static double SampleCpuUsagePercent(TimeSpan interval)
    {
        using var process = Process.GetCurrentProcess();

        var startCpuTime = process.TotalProcessorTime;
        var startTime = DateTime.UtcNow;

        Thread.Sleep(interval);

        process.Refresh();
        var endCpuTime = process.TotalProcessorTime;
        var endTime = DateTime.UtcNow;

        var cpuTimeDelta = (endCpuTime - startCpuTime).TotalMilliseconds;
        var timeDelta = (endTime - startTime).TotalMilliseconds;

        if (timeDelta <= 0)
        {
            return 0;
        }

        var rawUsage = cpuTimeDelta / timeDelta * 100.0;
        var normalized = rawUsage / Environment.ProcessorCount;

        return normalized;
    }

    public static void LogCpuUsage(ILogger logger, string context, TimeSpan interval)
    {
        var usage = SampleCpuUsagePercent(interval);

        logger.LogInformation(
            "CpuUsage Context={Context} Interval={IntervalMs}ms Usage={Usage:F1}%",
            context,
            interval.TotalMilliseconds,
            usage);
    }
}
C#

使い方の例です。

CpuLogger.LogCpuUsage(_logger, "バッチ開始前", TimeSpan.FromSeconds(1));

await RunBatchAsync();

CpuLogger.LogCpuUsage(_logger, "バッチ終了後", TimeSpan.FromSeconds(1));
C#

ログには、例えばこんな行が残ります。

「CpuUsage Context=バッチ開始前 Interval=1000ms Usage=15.2%」 「CpuUsage Context=バッチ終了後 Interval=1000ms Usage=85.7%」

ここでの重要ポイントは、

「CPU使用率を“文脈付きで”残す」 「どの処理のタイミングで高くなっているかを後から追えるようにする」

ということです。

実務での使いどころと注意点

「常に測り続ける」より「怪しいところをスポットで測る」

CPU使用率取得は便利ですが、 あまり細かく・頻繁に測りすぎると、それ自体が負荷になることがあります。

実務では、

  • 高負荷時にだけ測る
  • 特定のバッチや画面の前後で測る
  • 問題調査中だけ一時的にオンにする

といった「スポットライト的な使い方」がちょうどよいです。

また、CPU使用率が高いからといって、 必ずしも悪いとは限りません。

  • バッチ処理で「CPUをフルに使って短時間で終わらせたい」場面
  • 計算系の処理で「CPUを使うのが仕事」の場面

では、CPU使用率が高いこと自体は問題ではなく、 「他の処理を邪魔していないか」「全体の設計として許容できるか」がポイントになります。

ここでの重要ポイントは、

「CPU使用率は“悪者探し”ではなく、“設計が意図通りかを確認するための指標”」 「高いことが悪いのではなく、“高くて困っているかどうか”が大事」

ということです。

まとめ:CPU使用率取得は“重さを数字で語るためのユーティリティ”

CPU使用率取得の本質を一言で言うと、

「このアプリが、 ある時間のあいだに、 どれくらいCPUを使っていたかを数字で把握し、 重い処理や負荷のかかり方を客観的に語れるようにする」

ことです。

押さえておきたいポイントは次の通りです。

  • CPU使用率は「ある期間に対する割合」であり、差分計測が必須。
  • Process.TotalProcessorTime の前後差分と経過時間から使用率を計算できる。
  • マルチコア環境では、コア数を意識して正規化すると「全体に対する%」として見やすくなる。
  • ILogger と組み合わせた CPULogger を作り、「文脈付きでログに残す」と、どの処理が CPU を食っているかを後から分析できる。
  • CPU使用率は「高い=悪い」ではなく、「設計意図と合っているか」「他を邪魔していないか」を見るための指標。

ここまでイメージできていれば、 「なんか重い気がする」から一歩進んで、 数字で語れる CPU診断 に踏み出せます。

タイトルとURLをコピーしました