まずは一般論から。

このコメントで vctest に CPU 使用率を計測する機能を追加してほしいと言われています。

処理の経過時間は既に計測できているので CPU 時間(使用量)を計測できれば割り算するだけですが、世の中そんなに簡単ではありません。

Windows において CPU 時間を計測するには GetProcessTimes()/GetThreadTimes() Win32 API を使います。これを使うと、指定したプロセス/スレッドの生成時刻、終了時刻(すでに終了したプロセス/スレッドのみ)、カーネル時間、ユーザー時間が取得できます。これを使えばサックリ終了…ではありません。この API で取れる時間は約 1/64 秒の精度しかないのです。

たとえば、

int main(void)
{
    for (int i = 0; i < 10; i ++)
    {
        FILETIME ftCreation, ftExit, ftKernel, ftUser;
        ULARGE_INTEGER ulKernel, ulUser;

        GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, &ftKernel, &ftUser);
        ulKernel.LowPart  = ftKernel.dwLowDateTime;
        ulKernel.HighPart = ftKernel.dwHighDateTime;
        ulUser.LowPart    = ftUser.dwLowDateTime;
        ulUser.HighPart   = ftUser.dwHighDateTime;
        printf("%f %f\n", (double)ulKernel.QuadPart, (double)ulUser.QuadPart);
    }
	
    return 0;
}

というプログラムを実行すると、

E:\>GetProcessTimes.exe
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000

E:\>

などと出力されますし、カウントアップされたタイミングで表示しようとして

int main(void)
{
    int count = 0;
    FILETIME ftPrev = {0, 0};

    while (count < 10)
    {
        FILETIME ftCreation, ftExit, ftKernel, ftUser;
        ULARGE_INTEGER ulUser;
		
        for (int j = 0; j < 1000*1000; j++)
            ;

        GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, &ftKernel, &ftUser);
		
        if (memcmp(&ftPrev, &ftUser, sizeof(FILETIME)) != 0)
        {
            ulUser.LowPart    = ftUser.dwLowDateTime;
            ulUser.HighPart   = ftUser.dwHighDateTime;
            printf("%f\n", (double)ulUser.QuadPart);
            ftPrev = ftUser;
            count++;
        }
    }
	
    return 0;
}

というプログラム(最適化はオフにすること)を実行すると、

E:\>GetProcessTimes.exe
156001.000000
312002.000000
468003.000000
624004.000000
780005.000000
936006.000000
1092007.000000
1248008.000000
1404009.000000
1560010.000000

E:\>

などと表示されます。FILETIME 構造体は値を 100ns 単位で返すので、精度は 156,001 / 10,000,000 = 1/64.102… 秒となります。

計測しようとしているコーデックが1フレーム処理するのにかかる時間は数msであり、計測にはマイクロ秒精度が求められるため、これでは使い物になりません。

経過した実時間は高精度イベントタイマー (High Precision Event Timer; HPET) といって、ハードウェアが持っているソフトウェアとは無関係に正確に刻むタイマーを使うことでマイクロ秒より細かい精度で計測することができます(Windows だと QueryPerformanceCounter() Win32 API 経由で使える)が、プロセスが消費した時間は OS にしか分かりません。

なお、精度が 1/64 秒しかないのは別に Windows が悪いわけではなくて、他の OS でも似たようなものです。

その2に続く)

Trackback

only 1 comment untill now

  1. CPU 時間を正確に計測する(その2)

    その1の続き。 1フレームごとに時間を計測しようとするからマイクロ秒精度のタイマーが必要になってダメなんじゃないか、というのはある程度はその通りで、映像ファイル全体を処理…

Add your comment now