C# Tips | 日付・時間処理:ストップウォッチ

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

はじめに 「ストップウォッチ」は“処理時間を見える化する道具”

「この処理、どれくらい時間かかってるんだろう?」
「最適化したつもりだけど、本当に速くなった?」

こういう“処理時間の見える化”に使うのが、C# の System.Diagnostics.Stopwatch です。
「開始」「停止」「経過時間の取得」という、まさにストップウォッチそのものの API を持っています。

ここでは、
まず「一番シンプルな使い方」から入り、
「ラップ(区間計測)」「ユーティリティ化」「ハマりどころ」まで、
初心者向けにかみ砕いて説明します。


基本形:Stopwatch で処理時間を測る

一番シンプルな「開始→処理→停止→経過時間」

まずは「ある処理にどれくらい時間がかかったか」を測る最小パターンです。

using System;
using System.Diagnostics;

public class StopwatchSample
{
    public static void Main()
    {
        var sw = Stopwatch.StartNew(); // StartNew が一番ラク

        // 計測したい処理
        DoHeavyWork();

        sw.Stop();

        Console.WriteLine($"経過ミリ秒: {sw.ElapsedMilliseconds} ms");
        Console.WriteLine($"経過時間:   {sw.Elapsed}");
    }

    private static void DoHeavyWork()
    {
        // ダミーで少し時間のかかる処理
        for (int i = 0; i < 10_000_000; i++)
        {
            _ = Math.Sqrt(i);
        }
    }
}
C#

ここで押さえてほしいポイントは 3つです。

Stopwatch.StartNew() で「インスタンス生成+計測開始」を一気にやる。
Stopwatch.Stop() で計測を止める。
ElapsedTimeSpan)や ElapsedMilliseconds で経過時間を読む。

ElapsedTimeSpan なので、TotalSecondsTotalMilliseconds など、好きな単位で取り出せます。


ラップ計測:途中途中の区間時間を測る

「処理A」「処理B」「処理C」それぞれの時間を知りたい

1つのストップウォッチで、処理の区間ごとに時間を測ることもできます。

using System;
using System.Diagnostics;

public class StopwatchLapSample
{
    public static void Main()
    {
        var sw = Stopwatch.StartNew();

        DoWorkA();
        Console.WriteLine($"A 終了: {sw.ElapsedMilliseconds} ms");

        DoWorkB();
        Console.WriteLine($"B 終了: {sw.ElapsedMilliseconds} ms");

        DoWorkC();
        Console.WriteLine($"C 終了: {sw.ElapsedMilliseconds} ms");

        sw.Stop();
    }

    private static void DoWorkA()
    {
        for (int i = 0; i < 3_000_000; i++) { _ = Math.Sqrt(i); }
    }

    private static void DoWorkB()
    {
        for (int i = 0; i < 2_000_000; i++) { _ = Math.Sqrt(i); }
    }

    private static void DoWorkC()
    {
        for (int i = 0; i < 1_000_000; i++) { _ = Math.Sqrt(i); }
    }
}
C#

この書き方だと、
「開始から A 終了まで」「開始から B 終了まで」…の累積時間が分かります。

もし「A だけの時間」「B だけの時間」が欲しければ、
Elapsed の値を一時変数に覚えておいて差を取る、というやり方もあります。

var sw = Stopwatch.StartNew();

DoWorkA();
var afterA = sw.Elapsed;

DoWorkB();
var afterB = sw.Elapsed;

TimeSpan aOnly = afterA;
TimeSpan bOnly = afterB - afterA;
C#

「累積時間」と「区間時間」をどう使い分けるかを意識すると、
ストップウォッチの使い方が一段クリアになります。


ユーティリティ化:処理を渡すと時間を測ってくれるメソッド

「計測の型」を決めておくと毎回ラクになる

毎回 Stopwatch を書くのが面倒なら、
「処理を渡すと時間を測ってくれる」小さなユーティリティを作っておくと便利です。

using System;
using System.Diagnostics;

public static class MeasureUtil
{
    public static TimeSpan Measure(Action action)
    {
        var sw = Stopwatch.StartNew();

        action();

        sw.Stop();
        return sw.Elapsed;
    }
}
C#

使い方の例です。

TimeSpan elapsed = MeasureUtil.Measure(() =>
{
    DoHeavyWork();
});

Console.WriteLine($"処理時間: {elapsed.TotalMilliseconds} ms");
C#

非同期処理版も作れます。

using System.Threading.Tasks;

public static class MeasureUtilAsync
{
    public static async Task<TimeSpan> MeasureAsync(Func<Task> action)
    {
        var sw = Stopwatch.StartNew();

        await action();

        sw.Stop();
        return sw.Elapsed;
    }
}
C#
TimeSpan elapsed = await MeasureUtilAsync.MeasureAsync(async () =>
{
    await Task.Delay(1000);
});
C#

こうして「計測の型」を決めておくと、
「この処理、ちょっと時間測ってみよう」が一瞬で書けるようになります。


実務でのハマりどころとコツ

DateTime.Now ではなく Stopwatch を使う理由

「開始時刻を DateTime.Now で取って、終了時刻との差を取ればよくない?」
と思うかもしれませんが、実務では Stopwatch を使うほうが安全です。

DateTime.Now はシステム時計の変更(NTP 同期、手動変更など)の影響を受けます。
Stopwatch は「高精度タイマー」を使っていて、
「実際に経過した時間」を測るのに向いています。

「処理時間」「パフォーマンス計測」のような用途では、
基本的に Stopwatch 一択で考えてOKです。

Restart と Reset の違い

Stopwatch には Restart()Reset() があります。

Restart() … 経過時間を 0 にして、すぐに再スタート
Reset() … 経過時間を 0 にするだけ(止まった状態)

例えば、同じインスタンスで何度も計測したいときは Restart() が便利です。

var sw = new Stopwatch();

sw.Restart();
DoWorkA();
Console.WriteLine(sw.ElapsedMilliseconds);

sw.Restart();
DoWorkB();
Console.WriteLine(sw.ElapsedMilliseconds);
C#

この違いを知っておくと、「あれ、止まってるのに時間が増えてる?」みたいな混乱を避けられます。


まとめ 「ストップウォッチユーティリティ」は“なんとなく”をやめるための武器

ストップウォッチは、
「なんとなく遅い気がする」「たぶん速くなったはず」をやめて、
「何ミリ秒かかった」「何%速くなった」と言えるようにするための道具です。

押さえておきたいポイントはこうです。

Stopwatch.StartNew() → 処理 → Stopwatch.Stop()Elapsed という基本パターンを体に入れる。
ラップ計測では「累積時間」と「区間時間」を意識して Elapsed を使い分ける。
Measure(Action)MeasureAsync(Func<Task>) のようなユーティリティにしておくと、どこでもすぐ計測できる。
処理時間の計測には DateTime.Now ではなく Stopwatch を使う。
RestartReset の違いを理解して、同じインスタンスをうまく回す。

ここまで押さえれば、
「なんとなく速い/遅い」から卒業して、
“数字で語れる C# プログラマー”に一歩近づけます。

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