Windows環境における高精度時刻取得について
結論
Windows8未満
GetSystemTimeAsFileTime
Windows8以降
以下を比較検討のうえ、採用。
GetSystemTimePreciseAsFileTime
GetSystemTimeAsFileTime
上記に関するMicrosoftのまとめ
docs.microsoft.com
以下は検証の記録。
事前確認
事前知識
分解能
表現粒度を指す
例:12:11:59.999→分解能1ミリ、12:11:59→分解能1秒
精度
判別可能な最小の断面
例:0.111,0.112,0.112 ... →精度1ミリ、0.110, 0.125,0.140... 精度15ミリ
検証環境
OS:Windows10 Pro
分解能の確認
分解能の確認コード
// SystimeSample.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include <Windows.h> #include <stdio.h> #include <mmsystem.h> #pragma comment(lib, "winmm.lib") int main() { DWORD dwTimeAdjustment; DWORD dwTimeIncrement; BOOL bTimeAdjustmentDisabled; GetSystemTimeAdjustment(&dwTimeAdjustment, &dwTimeIncrement, &bTimeAdjustmentDisabled); printf("分解能(クロック割り込み間隔):%f ms\n", (double)dwTimeIncrement / 10000.0); printf("調整機能:%s\n", (bTimeAdjustmentDisabled ? "True" : "False")); printf("加算値(単位:ナノ秒):%f nano \n", GetSystemTimeAdjustment); TIMECAPS ptc; timeGetDevCaps(&ptc, sizeof(ptc)); printf("マルチメディアタイマー分解能:%d ms\n", ptc.wPeriodMin); LARGE_INTEGER lpFrequency; QueryPerformanceFrequency(&lpFrequency); printf("高分解能カウンター分解能:%f ms\n", 1000.0 / lpFrequency.QuadPart); return 0; }
結果
分解能(クロック割り込み間隔):15.625000 ms
調整機能:True
加算値(単位:ナノ秒):0.000000 nano
マルチメディアタイマー分解能:1 ms
高分解能カウンター分解能:0.000428 ms
上記はあくまでRTC(リアルタイムクロック・・・ハードウェア クロック)についての情報ぽい。
検証方法
APIを10万回連続で実行し、ミリ秒部分の更新が起きた際に、前回との差を記録する。
Windows previous versions documentation | Microsoft Docsによる計測結果
差分(ms) | 出現回数 |
---|---|
1 | 99995 |
2 | 1 |
12 | 1 |
15 | 1 |
32 | 2 |
Windows previous versions documentation | Microsoft Docsによる計測結果
差分(ms) | 出現回数 |
---|---|
1 | 99993 |
2 | 2 |
18 | 1 |
22 | 1 |
29 | 1 |
30 | 1 |
31 | 1 |
Windows previous versions documentation | Microsoft Docsによる計測結果
差分(ms) | 出現回数 |
---|---|
1 | 99997 |
2 | 2 |
32 | 1 |
GetSystemTimePreciseAsFileTime関数による計測結果
さらに調べると・・・Win8以降、高精度のシステム時刻取得API「GetSystemTimePreciseAsFileTime」が追加されている。
差分(ms) | 出現回数 |
---|---|
1 | 99995 |
4 | 1 |
3 | 1 |
6 | 1 |
2 | 1 |
31 | 1 |
環境によっては、GetSystemTimePreciseAsFileTimeが遅い場合もあるらしい。
GetSystemTimePreciseAsFileTimeが1ミリ秒の精度を持つのはドキュメントを読むと納得出来るが、
他の関数も1ミリ秒の精度が出ているのはなぜだろうか。
10万回の検証コード
// SystimeSample.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include <Windows.h> #include <stdio.h> #include "stdafx.h" #include <Windows.h> #include <stdio.h> #include <unordered_map> #include <iostream> const int N = 100001; const int ignoreN = 0; int main() { int secondAndMillseconds[N][2]; std::fill(secondAndMillseconds[0], secondAndMillseconds[N], 0); SYSTEMTIME tm, oldTime; FILETIME ft; GetSystemTimePreciseAsFileTime(&ft); //GetSystemTimeAsFileTime(&ft); //GetSystemTime(&tm); //GetLocalTime(&tm); FileTimeToSystemTime(&ft, &tm); oldTime = tm; for (int i = 0; i < N;) { GetSystemTimePreciseAsFileTime(&ft); //GetSystemTimeAsFileTime(&ft); FileTimeToSystemTime(&ft, &tm); //GetSystemTime(&tm); //GetLocalTime(&tm); if (tm.wMilliseconds != oldTime.wMilliseconds) { // printf("%d:%d:%d.%d\n", tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds); secondAndMillseconds[i][0] = tm.wSecond; secondAndMillseconds[i][1] = tm.wMilliseconds; oldTime = tm; i++; } } int oldSecond = 0; int oldMillSecond = 0; int second = 0; int millSecond = 0; int diffMillSecond = 0; // 差分別の出現回数map std::unordered_map<int, int> map; int startIndex = ignoreN; oldSecond = secondAndMillseconds[startIndex][0]; oldMillSecond = secondAndMillseconds[startIndex][1]; for (int i = startIndex + 1; i < N; i++) { // 0秒と59秒を比べる状況の考慮 if (secondAndMillseconds[i][0] == 0 && oldSecond == 59) { second = 60; } else { second = secondAndMillseconds[i][0]; } millSecond = secondAndMillseconds[i][1]; diffMillSecond = (second * 1000 + millSecond) - (oldSecond * 1000 + oldMillSecond); if (map[diffMillSecond] == 0) { map[diffMillSecond] = 1; } else { map[diffMillSecond] = map[diffMillSecond] + 1; } oldSecond = secondAndMillseconds[i][0]; oldMillSecond = secondAndMillseconds[i][1]; } for (auto itr = map.begin(); itr != map.end(); ++itr) { std::cout << itr->first // キーを表示 << "," << itr->second << "\n"; // 値を表示 } return 0; }