目次
概要
プログラムの実行速度や、特定の処理にかかった時間を計測するために System.Diagnostics.Stopwatch クラスを使用します。 DateTime の差分を使用するよりも高精度であり、一時停止(Stop)や再開(Start)による時間の積算も簡単に行えます。
仕様(入出力)
- 入力
- 計測開始、一時停止、再開のトリガー
- 出力
- 経過時間(
TimeSpan) - 実行中かどうかの状態(
bool)
- 経過時間(
- 機能
- OSの高分解能パフォーマンスカウンターを利用した高精度な計測。
- 停止後に再度開始することで、経過時間を累積して計測可能。
基本の使い方
インスタンスを作成してスタートし、ストップした時点での Elapsed プロパティを参照します。
using System.Diagnostics;
// 計測開始
var sw = Stopwatch.StartNew();
// ... 何らかの処理 ...
// 計測停止
sw.Stop();
// 結果の表示
Console.WriteLine($"経過時間: {sw.Elapsed}");
コード全文
処理を一時停止し、少し時間を置いてから再開することで、Stopwatchが「計測中の時間のみ」を積算していることを確認するコンソールアプリケーションです。
using System;
using System.Diagnostics;
using System.Threading;
class Program
{
static void Main()
{
Console.WriteLine("--- 計測を開始します ---");
// 1. インスタンス生成と計測開始を同時に行う
Stopwatch sw = Stopwatch.StartNew();
Console.WriteLine($"[State] IsRunning: {sw.IsRunning}");
// 処理A(計測対象:1秒)
Console.WriteLine("処理Aを実行中 (1000ms)...");
Thread.Sleep(1000);
// 2. 一時停止
sw.Stop();
Console.WriteLine($"[State] IsRunning: {sw.IsRunning}");
// 現時点での経過時間を表示
// "G" 書式指定子は、一般的な時間形式(d.hh:mm:ss.fffffff)で出力
Console.WriteLine($"中間タイム: {sw.Elapsed:G}");
Console.WriteLine("\n--- 計測対象外の時間 (待機中: 2000ms) ---\n");
Thread.Sleep(2000); // この時間は計測に含まれない
// 3. 計測再開 (Startメソッドは積算となる)
Console.WriteLine("--- 計測を再開します ---");
sw.Start();
Console.WriteLine($"[State] IsRunning: {sw.IsRunning}");
// 処理B(計測対象:1.5秒)
Console.WriteLine("処理Bを実行中 (1500ms)...");
Thread.Sleep(1500);
// 4. 最終停止
sw.Stop();
Console.WriteLine($"[State] IsRunning: {sw.IsRunning}");
// 最終的な経過時間を取得
TimeSpan totalResult = sw.Elapsed;
Console.WriteLine($"\n最終結果: {totalResult:G}");
Console.WriteLine($"ミリ秒換算: {sw.ElapsedMilliseconds} ms");
}
}
実行結果例
--- 計測を開始します ---
[State] IsRunning: True
処理Aを実行中 (1000ms)...
[State] IsRunning: False
中間タイム: 0:00:00:01.0123456
--- 計測対象外の時間 (待機中: 2000ms) ---
--- 計測を再開します ---
[State] IsRunning: True
処理Bを実行中 (1500ms)...
[State] IsRunning: False
最終結果: 0:00:00:02.5234567
ミリ秒換算: 2523 ms
※ 実行環境によりミリ秒以下の誤差が発生します。
カスタムポイント
- リセットして再利用
sw.Reset()を呼ぶと計測時間がゼロに戻り、停止状態になります。同じインスタンスを使い回して別の計測を行う場合に使用します。sw.Restart()はゼロリセットと同時に再スタートを行います。
- 表示形式の変更
sw.Elapsed.ToString(@"hh\:mm\:ss")のように、TimeSpanのカスタム書式指定を使って必要な桁数だけを表示できます。
- 簡易計測プロパティ
- 単に「何ミリ秒かかったか」だけを知りたい場合は、
TimeSpanを経由せずsw.ElapsedMillisecondsプロパティ(long型)を使用するのが手軽です。
- 単に「何ミリ秒かかったか」だけを知りたい場合は、
注意点
- DateTimeとの違い
DateTime.Nowの差分で計算すると、OSのシステム時刻変更の影響を受けたり、精度が粗かったりします。処理時間の計測には必ずStopwatchを使用してください。
- IsRunningの確認
Start()は計測中であっても例外を出しませんが、Stop()も停止中に呼んでも問題ありません。ただし、論理的に状態を把握したい場合はIsRunningプロパティを確認してください。
- 微細なオーバーヘッド
Stopwatchクラス自体の操作にもごくわずかなコストがかかります。ナノ秒単位の厳密な計測が必要なループ内などでは留意する必要があります。
応用
処理ブロックの計測クラス
IDisposable を利用して、using ブロックを抜けた瞬間に自動的に時間を計測・出力するヘルパークラスです。
using System;
using System.Diagnostics;
public class MeasureScope : IDisposable
{
private readonly Stopwatch _sw;
private readonly string _name;
public MeasureScope(string name)
{
_name = name;
_sw = Stopwatch.StartNew();
}
public void Dispose()
{
_sw.Stop();
Console.WriteLine($"[{_name}] 完了: {_sw.ElapsedMilliseconds} ms");
}
}
// 使用例
// using (new MeasureScope("重いDB処理"))
// {
// // 計測したい処理
// }
まとめ
Stopwatch クラスは、アプリケーションのパフォーマンスチューニングやログ記録において最も基本かつ重要なツールです。Start と Stop を繰り返すことで、特定の処理時間だけを積み上げて計測できるのが最大の特徴です。単純な DateTime の引き算ではなく、このクラスを利用することで、システム時刻の変更に影響されない高精度なベンチマークが可能になります。
