Microsoft MVP성태의 닷넷 이야기
Windows: 269. GetSystemTimeAsFileTime과 GetSystemTimePreciseAsFileTime의 차이점 [링크 복사], [링크+제목 복사],
조회: 7165
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 6개 있습니다.)
Windows: 120. 윈도우 운영체제의 시간 함수 (1) - GetTickCount와 timeGetTime의 차이점
; https://www.sysnet.pe.kr/2/0/11063

Windows: 121. 윈도우 운영체제의 시간 함수 (2) - Sleep 함수의 동작 방식
; https://www.sysnet.pe.kr/2/0/11065

Windows: 122. 윈도우 운영체제의 시간 함수 (3) - QueryInterruptTimePrecise, QueryInterruptTime 함수
; https://www.sysnet.pe.kr/2/0/11066

Windows: 123. 윈도우 운영체제의 시간 함수 (4) - RTC, TSC, PM Clock, HPET Timer
; https://www.sysnet.pe.kr/2/0/11067

Windows: 124. 윈도우 운영체제의 시간 함수 (5) - TSC(Time Stamp Counter)와 QueryPerformanceCounter
; https://www.sysnet.pe.kr/2/0/11068

Windows: 269. GetSystemTimeAsFileTime과 GetSystemTimePreciseAsFileTime의 차이점
; https://www.sysnet.pe.kr/2/0/13802




Windows - GetSystemTimeAsFileTime과 GetSystemTimePreciseAsFileTime의 차이점

우선, 이 차이점을 이해하려면 아래의 글을 먼저 읽어주시고. ^^

윈도우 운영체제의 시간 함수 (1) - GetTickCount와 timeGetTime의 차이점
; https://www.sysnet.pe.kr/2/0/11063

위의 글을 이해했다면 이제 GetSystemTimeAsFileTime의 동작 방식도 쉽게 알 수 있습니다.

GetSystemTimeAsFileTime function (sysinfoapi.h)
; https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime

즉, GetSystemTimeAsFileTime은 timer interrupt가 tick을 업데이트하는 주기로 시간이 업데이트되는 것인데요, 따라서 다음과 같은 식으로 테스트해 보면,

#include <vector>
#include <Windows.h>

using namespace std;

ULONGLONG SubtractFileTime(const FILETIME& ftA, const FILETIME& ftB)
{
    ULARGE_INTEGER a, b;
    a.LowPart = ftA.dwLowDateTime;
    a.HighPart = ftA.dwHighDateTime;

    b.LowPart = ftB.dwLowDateTime;
    b.HighPart = ftB.dwHighDateTime;

    return (a.QuadPart - b.QuadPart);
}

int main()
{
    int count = 1000000;
    vector<FILETIME> ticks;

    for (int i = 0; i < count; i++)
    {
        FILETIME ft;
        GetSystemTimeAsFileTime(&ft);

        ticks.push_back(ft);
    }

    FILETIME oldTime = ticks[0];
    ULONGLONG elapsed;
    for (int i = 1; i < count; i++)
    {
        elapsed = SubtractFileTime(ticks[i], oldTime);
        oldTime = ticks[i];

        if (elapsed != 0)
        {
            printf("%lld\n", elapsed);
        }
    }
}

/* 출력 결과: Current timer interval" == 1ms인 경우
10049
9912
10000
10023
9990
9992
10069
9931
9990
10007
*/

대충, 10,000 범위로 값이 툭툭 튀고 있는데요, 저 값의 정확한 의미는 QueryPerformanceFrequency가 반환한 값이 있어야 해석이 가능합니다.

bool g_IsHighResolution = false;

__int64 GetQPCFreq()
{
    LARGE_INTEGER qpcRate;
    g_IsHighResolution = QueryPerformanceFrequency(&qpcRate); // 대개의 경우 g_IsHighResolution == true
    return qpcRate.QuadPart;
}

__int64 frequency = GetQPCFreq();
printf("QPC frequency: %lld\n", frequency); // 출력 결과: QPC frequency: 10000000

위의 결과에 따라 GetSystemTimeAsFileTime이 반환한 값의 1 단위는 1 / 10,000,000 초(0.1 마이크로 초, 100 나노 초)에 해당합니다. 따라서 10,000 주기로 값이 튀는 것은 1 / 1,000초, 즉 1ms 주기로 발생하는 timer interrupt마다 GetSystemTimeAsFileTime의 값이 바뀐다는 것을 의미합니다.

만약 timer interrupt 주기가 15.6ms인 경우라면, 약 156,000 단위로 값이 튀는 현상을 볼 수 있습니다.

결국 timer interrupt가 발생한 바로 그 순간에 100 나노 초 단위의 정밀도로 그 시간을 보관하게 되지만, 이후 1ms가 지나기까지는 그 값이 변경되지 않다가, 1ms가 지나서야 다시 그 시점의 시간을 100 나노 초 단위로 보여주는 식입니다.




GetSystemTimePreciseAsFileTime은, 예상할 수 있겠지만 Precise라는 단어가 들어간 것에서 좀 더 정밀한 시간을 나타낼 것이라고 예상할 수 있습니다.

GetSystemTimePreciseAsFileTime function (sysinfoapi.h)
; https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime

실제로, 위의 예제 코드를 GetSystemTimeAsFileTime을 호출하는 것만 GetSystemTimePreciseAsFileTime으로 바꿔 실행해 보면,

int main()
{
    int count = 10;
    vector<FILETIME> ticks;

    for (int i = 0; i < count; i++)
    {
        FILETIME ft;
        GetSystemTimePreciseAsFileTime(&ft);

        ticks.push_back(ft);
    }

    // ...[생략]...
}

/* 출력 결과: Current timer interval"의 설정과 무관하게!
3
2
1
2
1
1
1

대략 0.1us마다 값이 튀는 것을 볼 수 있습니다. 즉 timer interrupt가 발생하는 것과 무관하게 현재 시간을 100ns 단위의 정밀도로 반환하고 있는 것입니다.

이 함수는 Windows 8 / Windows Server 2012부터 구현하고 있는데요, 이게 어떻게 가능하게 된 것일까요? ^^ 일단 윈도우 소스코드가 없어 구체적으로 어떻게 구현돼 있는지는 알 수 없지만, 그냥 제 추측으로 적어보자면... ^^

아마도, timer interrupt가 발생하는 주기로 기존처럼 시간을 업데이트하고 있지만, 바로 그 시점의 rdtsc 값을 보관한 다음 이후 GetSystemTimePreciseAsFileTime을 호출할 때 rdtsc의 변화를 계산해 마지막 timer interrupt가 발생한 시점의 값과 더해 반환하는 식이... 아닐까 싶습니다.

물론, 그렇게 하면 요즘 CPU의 경우 GHz 주기로 시간 정밀도가 나올 수 있는데요, 하지만 근래의 Windows 운영체제는 그 값을 정규화시켜 100ns에 맞춰서 제공하고 있습니다.

아무튼, 1 ~ 15.6ms 정도의 정밀도로 상관없다면 GetSystemTimeAsFileTime을 사용하고, 그 이상의 정밀도가 필요하다면 (보통) 0.1us 정밀도를 갖는 GetSystemTimePreciseAsFileTime을 사용하면 됩니다. (주의할 사항이 있는데, 일부 시스템에서는 Precise 함수가 정상적인 값을 반환하지 않는 문제가 있으므로, GetSystemTimeAsFileTime으로 보완하는 코드가 필요합니다.)




참고로, QueryPerformanceFrequency 함수가 대개의 경우 10000000을 반환하지만 환경에 따라 다른 값을 반환하기도 합니다. 가령 예전에 질문하셨던 분도 그렇고, 아래의 글을 테스트할 때만 해도,

윈도우 운영체제의 시간 함수 (5) - TSC(Time Stamp Counter)와 QueryPerformanceCounter
; https://www.sysnet.pe.kr/2/0/11068

QueryPerformanceFrequency가 3328129를 반환했었는데요, 이 차이는 Windows 10 build 1809부터 바뀐 것이니 유의하시기 바랍니다.

Windows 10부터 바뀐 QueryPerformanceFrequency, QueryPerformanceCounter
; https://www.sysnet.pe.kr/2/0/13035





[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/7/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 106  107  [108]  109  110  111  112  113  114  115  116  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11257정성태7/31/201719866.NET Framework: 667. bypassTrustedAppStrongNames 옵션 설명파일 다운로드1
11256정성태7/25/201721774디버깅 기술: 90. windbg의 lm 명령으로 보이지 않는 .NET 4.0 ClassLibrary를 명시적으로 로드하는 방법 [1]
11255정성태7/18/201724289디버깅 기술: 89. Win32 Debug CRT Heap Internals의 0xBAADF00D 표시 재현 [1]파일 다운로드3
11254정성태7/17/201720710개발 환경 구성: 322. "Visual Studio Emulator for Android" 에뮬레이터를 "Android Studio"와 함께 쓰는 방법
11253정성태7/17/201721327Math: 21. "Coding the Matrix" 문제 2.5.1 풀이 [1]파일 다운로드1
11252정성태7/13/201719086오류 유형: 411. RTVS 또는 PTVS 실행 시 Could not load type 'Microsoft.VisualStudio.InteractiveWindow.Shell.IVsInteractiveWindowFactory2'
11251정성태7/13/201718540디버깅 기술: 88. windbg 분석 - webengine4.dll의 MgdExplicitFlush에서 발생한 System.AccessViolationException의 crash 문제 (2)
11250정성태7/13/201722172디버깅 기술: 87. windbg 분석 - webengine4.dll의 MgdExplicitFlush에서 발생한 System.AccessViolationException의 crash 문제 [1]
11249정성태7/12/201719888오류 유형: 410. LoadLibrary("[...].dll") failed - The specified procedure could not be found.
11248정성태7/12/201726444오류 유형: 409. pip install pefile - 'cp949' codec can't decode byte 0xe2 in position 208687: illegal multibyte sequence
11247정성태7/12/201720762오류 유형: 408. SqlConnection 객체 생성 시 무한 대기 문제파일 다운로드1
11246정성태7/11/201718801VS.NET IDE: 118. Visual Studio - 다중 폴더에 포함된 파일들에 대한 "Copy to Output Directory"를 한 번에 설정하는 방법
11245정성태7/10/201724586개발 환경 구성: 321. Visual Studio Emulator for Android 소개 [2]
11244정성태7/10/201724753오류 유형: 407. Visual Studio에서 ASP.NET Core 실행할 때 dotnet.exe 프로세스의 -532462766 오류 발생 [1]
11243정성태7/10/201721543.NET Framework: 666. dotnet.exe - 윈도우 운영체제에서의 .NET Core 버전 찾기 규칙
11242정성태7/8/201721087제니퍼 .NET: 27. 제니퍼 닷넷 적용 사례 (7) - 노후된 스토리지 장비로 인한 웹 서비스 Hang (멈춤) 현상
11241정성태7/8/201719746오류 유형: 406. Xamarin 빌드 에러 XA5209, APT0000
11240정성태7/7/201723528.NET Framework: 665. ClickOnce를 웹 브라우저를 이용하지 않고 쿼리 문자열을 전달하면서 실행하는 방법 [3]파일 다운로드1
11239정성태7/6/201724179.NET Framework: 664. Protocol Handler - 웹 브라우저에서 데스크톱 응용 프로그램을 실행하는 방법 [5]파일 다운로드1
11238정성태7/6/201721677오류 유형: 405. NT 서비스 시작 시 "Error 1067: The process terminated unexpectedly." 오류 발생 [2]
11237정성태7/5/201723358.NET Framework: 663. C# - PDB 파일 경로를 PE 파일로부터 얻는 방법파일 다운로드1
11236정성태7/4/201727076.NET Framework: 662. C# - VHD/VHDX 가상 디스크를 마운트하지 않고 파일을 복사하는 방법파일 다운로드1
11235정성태6/29/201721264Math: 20. Matlab/Octave로 Gram-Schmidt 정규 직교 집합 구하는 방법
11234정성태6/29/201718786오류 유형: 404. SharePoint 2013 설치 과정에서 "The username is invalid The account must be a valid domain account" 오류 발생
11233정성태6/28/201718657오류 유형: 403. SharePoint Server 2013을 Windows Server 2016에 설치할 때 .NET 4.5 설치 오류 발생
11232정성태6/28/201719571Windows: 144. Windows Server 2016에 Windows Identity Extensions을 설치하는 방법
... 106  107  [108]  109  110  111  112  113  114  115  116  117  118  119  120  ...