DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

より高い精度で時間を計測する

ここでは、ベンチマークを計るときなどのように、ある時点からある時点までの時間をより高い精度で計測する方法を紹介します。

Stopwatchクラス

.NET Framework 2.0以降では、Stopwatchクラスを使うことにより、経過時間を高い精度で計測することができます。

使い方は非常に簡単で、Startメソッドで計測を開始し、Stopメソッドで停止し、Elapsedプロパティで経過時間を取得します。

VB.NET
コードを隠すコードを選択
'Stopwatchオブジェクトを作成する 
Dim sw As New System.Diagnostics.Stopwatch()
'ストップウォッチを開始する 
sw.Start()

'次のようにStartNewメソッドを使うと、上の2行と同じことが1行でできる 
'Dim sw As System.Diagnostics.Stopwatch = System.Diagnostics.Stopwatch.StartNew()

'時間を計測したい処理がここにあるものとする 
For i As Integer = 0 To 9
    System.Threading.Thread.Sleep(100)
Next

'ストップウォッチを止める 
sw.Stop()

'結果を表示する 
Console.WriteLine(sw.Elapsed)
C#
コードを隠すコードを選択
//Stopwatchオブジェクトを作成する
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
//ストップウォッチを開始する
sw.Start();

//次のようにStartNewメソッドを使うと、上の2行と同じことが1行でできる
//System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

//時間を計測したい処理がここにあるものとする
for (int i = 0; i < 10; i++)
{
    System.Threading.Thread.Sleep(100);
}

//ストップウォッチを止める
sw.Stop();

//結果を表示する
Console.WriteLine(sw.Elapsed);

Stopwatchの精度

Stopwatchクラスは、高解像力(高分解能)パフォーマンスカウンタがサポートされていればそれを使い、サポートされていなければシステムタイマを使用します。Stopwatchクラスが高解像力パフォーマンスカウンタを使用している時は、Stopwatch.IsHighResolutionフィールドがTrueになります。

また、タイマの精度を示すタイマ頻度は、Stopwatch.Frequencyフィールドで分かります。Frequencyフィールドの値は、1秒あたりのタイマ刻みの数(カウンタのカウント数)です。

Elapsed、ElapsedMilliseconds、ElapsedTicksプロパティの違い

Stopwatchクラスには、経過時間を返す3つのプロパティがあります。Elapsedプロパティは、経過時間をTimeSpan型で返します。ElapsedMillisecondsプロパティは、ミリ秒単位の整数を返します。

ElapsedTicksプロパティは、タイマ刻みの数を返します。この値を秒単位に変換するには、Frequencyフィールドで割る必要があります。しかしElapsedとElapsedMillisecondsプロパティの値はElapsedTicksをFrequencyで割ったものですので、自分で計算する必要はないでしょう。

複数回計測する

1つのStopwatchオブジェクトでStartとStopを繰り返すと、経過時間は累計されていきます。経過時間を0にリセットするには、Resetメソッドを呼び出します。

以下の例で、StopしてからStartで再開するまでにResetでリセットしないと経過時間が累計されることを確認してください。

VB.NET
コードを隠すコードを選択
'ストップウォッチを開始する 
Dim sw As System.Diagnostics.Stopwatch = System.Diagnostics.Stopwatch.StartNew()

'1秒待機 
System.Threading.Thread.Sleep(1000)

'ストップウォッチを止める 
sw.Stop()

'結果を表示する 
Console.WriteLine(sw.Elapsed)
'結果例: 00:00:00.9990679 

'ストップウォッチを再開する 
sw.Start()

'1秒待機 
System.Threading.Thread.Sleep(1000)

'結果を表示する 
Console.WriteLine(sw.Elapsed)
'結果例: 00:00:01.9989872 

'ストップウォッチをリセットしてから再開する 
sw.Reset()
sw.Start()

'1秒待機 
System.Threading.Thread.Sleep(1000)

'結果を表示する 
Console.WriteLine(sw.Elapsed)
'結果例: 00:00:00.9999094
C#
コードを隠すコードを選択
//ストップウォッチを開始する
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

//1秒待機
System.Threading.Thread.Sleep(1000);

//ストップウォッチを止める
sw.Stop();

//結果を表示する
Console.WriteLine(sw.Elapsed);
//結果例: 00:00:00.9990679

//ストップウォッチを再開する
sw.Start();

//1秒待機
System.Threading.Thread.Sleep(1000);

//結果を表示する
Console.WriteLine(sw.Elapsed);
//結果例: 00:00:01.9989872

//ストップウォッチをリセットしてから再開する
sw.Reset();
sw.Start();

//1秒待機
System.Threading.Thread.Sleep(1000);

//結果を表示する
Console.WriteLine(sw.Elapsed);
//結果例: 00:00:00.9999094

.NET Framework 1.1以前で、QueryPerformanceCounterを使う

Stopwatchクラスが使えない場合は、Win32 APIのQueryPerformanceCounterを使います。QueryPerformanceCounterの使い方は、「[HOW TO] Visual C# .NET で、QueryPerformanceCounter を使用してコードの時間を計測する方法」や「pinvoke.net: QueryPerformanceCounter (kernel32)」で説明されています。

以下に簡単な例を示します。

VB.NET
コードを隠すコードを選択
<System.Runtime.InteropServices.DllImport("kernel32.dll")> _
Private Shared Function QueryPerformanceCounter( _
    ByRef lpPerformanceCount As Long) As Boolean
End Function

<System.Runtime.InteropServices.DllImport("kernel32.dll")> _
Private Shared Function QueryPerformanceFrequency( _
    ByRef lpFrequency As Long) As Boolean
End Function

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
    Handles Button1.Click

    '開始時のカウンタを取得する 
    Dim startCount As Long
    If Not QueryPerformanceCounter(startCount) Then
        Console.WriteLine("高解像力パフォーマンスカウンタが未サポート")
        Exit Sub
    End If

    '時間を計る処理がここにあるものとする 
    System.Threading.Thread.Sleep(3000)

    '終了時のカウンタを取得する 
    Dim endCount As Long
    QueryPerformanceCounter(endCount)

    '1秒あたりのカウント数を取得する 
    Dim frequency As Long
    QueryPerformanceFrequency(frequency)

    '経過時間を秒に変換して表示する 
    Console.WriteLine("{0}秒", CDbl((endCount - startCount)) / frequency)
End Sub
C#
コードを隠すコードを選択
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool QueryPerformanceFrequency(out long lpFrequency);

//Button1のClickイベントハンドラ
private void button1_Click(object sender, System.EventArgs e)
{
    //開始時のカウンタを取得する
    long startCount;
    if (!QueryPerformanceCounter(out startCount))
    {
        Console.WriteLine("高解像力パフォーマンスカウンタが未サポート");
        return;
    }

    //時間を計る処理がここにあるものとする
    System.Threading.Thread.Sleep(3000);

    //終了時のカウンタを取得する
    long endCount;
    QueryPerformanceCounter(out endCount);

    //1秒あたりのカウント数を取得する
    long frequency;
    QueryPerformanceFrequency(out frequency);

    //経過時間を秒に変換して表示する
    Console.WriteLine("{0}秒", (double)(endCount - startCount) / frequency);
}

QueryPerformanceCounterが使えない(高解像力パフォーマンスカウンタがサポートされていない)のであれば、システムタイマの値を使用します。具体的には、DateTime.Now.Ticksなどの値を使用します。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。