.NET 타이머가 15ms 분해능으로 제한되는 이유는 무엇입니까?
같은 것을 사용하여 15ms마다 한 번 이상 콜백 함수를 호출하는 것에 대해 질문하고 System.Threading.Timer
있습니다. 나는 System.Diagnostics.Stopwatch
또는 같은 것을 사용하여 코드 조각의 시간을 정확히 측정하는 방법에 대해 묻는 것이 아닙니다 QueryPerformanceCounter
.
또한 관련 질문을 읽었습니다.
정확한 Windows 타이머? System.Timers.Timer ()는 15msec로 제한됩니다.
어느 쪽도 내 질문에 대한 유용한 답변을 제공하지 않습니다.
또한 권장되는 MSDN 기사 인 Implement a Continuously Updating, High-Resolution Time Provider for Windows 는 연속적인 틱 스트림을 제공하기보다는 타이밍에 관한 것입니다.
그 말로. . .
.NET 타이머 개체에 대한 잘못된 정보가 많이 있습니다. 예를 들어, System.Timers.Timer
"서버 응용 프로그램에 최적화 된 고성능 타이머"로 청구됩니다. 그리고 System.Threading.Timer
어떻게 든 이등 시민으로 간주됩니다. 일반적인 통념은 System.Threading.Timer
Windows Timer Queue Timer를 둘러싼 래퍼 이며 System.Timers.Timer
완전히 다른 것입니다.
현실은 많이 다릅니다. System.Timers.Timer
주변의 얇은 구성 요소 래퍼 System.Threading.Timer
(Reflector 또는 ILDASM을 사용하여 내부를 들여다 System.Timers.Timer
보면에 대한 참조가 System.Threading.Timer
표시됨) 일 뿐이며 자동 스레드 동기화를 제공하는 코드가 있으므로이를 수행 할 필요가 없습니다.
System.Threading.Timer
, Timer Queue Timer의 래퍼 가 아닙니다 . 적어도 .NET 2.0에서 .NET 3.5까지 사용 된 2.0 런타임에서는 그렇지 않습니다. 공유 소스 CLI를 사용하여 몇 분 동안 런타임이 타이머 대기열 타이머와 유사한 자체 타이머 대기열을 구현하지만 실제로 Win32 함수를 호출하지 않음을 보여줍니다.
.NET 4.0 런타임도 자체 타이머 큐를 구현하는 것으로 보입니다. 내 테스트 프로그램 (아래 참조)은 .NET 3.5에서와 유사한 결과를 .NET 4.0에서 제공합니다. Timer Queue Timer에 대한 자체 관리 래퍼를 만들고 1ms 해상도 (아주 좋은 정확도로)를 얻을 수 있음을 증명 했으므로 CLI 소스를 잘못 읽지 않을 것 같네요.
두 가지 질문이 있습니다.
첫째, 런타임의 타이머 큐 구현이 그렇게 느리게하는 원인은 무엇입니까? 15ms 이상의 분해능을 얻을 수없고 정확도는 -1 ~ + 30ms 범위에있는 것 같습니다. 즉, 24ms를 요청하면 23 ~ 54ms 간격으로 틱이 표시됩니다. 답을 찾기 위해 CLI 소스에 더 많은 시간을 할애 할 수 있다고 생각하지만 여기 누군가가 알고있을 것이라고 생각했습니다.
둘째, 이것이 대답하기가 더 어렵다는 것을 알고 있습니다. Timer Queue Timer를 사용하지 않는 이유는 무엇입니까? .NET 1.x는 이러한 API가없는 Win9x에서 실행되어야했지만 Windows 2000 이후로 존재했습니다. 올바르게 기억한다면 .NET 2.0의 최소 요구 사항이었습니다. CLI가 비 Windows 상자에서 실행되어야했기 때문입니까?
내 타이머 테스트 프로그램 :
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace TimerTest
{
class Program
{
const int TickFrequency = 5;
const int TestDuration = 15000; // 15 seconds
static void Main(string[] args)
{
// Create a list to hold the tick times
// The list is pre-allocated to prevent list resizing
// from slowing down the test.
List<double> tickTimes = new List<double>(2 * TestDuration / TickFrequency);
// Start a stopwatch so we can keep track of how long this takes.
Stopwatch Elapsed = Stopwatch.StartNew();
// Create a timer that saves the elapsed time at each tick
Timer ticker = new Timer((s) =>
{
tickTimes.Add(Elapsed.ElapsedMilliseconds);
}, null, 0, TickFrequency);
// Wait for the test to complete
Thread.Sleep(TestDuration);
// Destroy the timer and stop the stopwatch
ticker.Dispose();
Elapsed.Stop();
// Now let's analyze the results
Console.WriteLine("{0:N0} ticks in {1:N0} milliseconds", tickTimes.Count, Elapsed.ElapsedMilliseconds);
Console.WriteLine("Average tick frequency = {0:N2} ms", (double)Elapsed.ElapsedMilliseconds / tickTimes.Count);
// Compute min and max deviation from requested frequency
double minDiff = double.MaxValue;
double maxDiff = double.MinValue;
for (int i = 1; i < tickTimes.Count; ++i)
{
double diff = (tickTimes[i] - tickTimes[i - 1]) - TickFrequency;
minDiff = Math.Min(diff, minDiff);
maxDiff = Math.Max(diff, maxDiff);
}
Console.WriteLine("min diff = {0:N4} ms", minDiff);
Console.WriteLine("max diff = {0:N4} ms", maxDiff);
Console.WriteLine("Test complete. Press Enter.");
Console.ReadLine();
}
}
}
아마도 여기에 링크 된 문서가 약간 설명합니다. 약간 건조해서 빨리 찾아 봤습니다 :)
소개 인용 :
시스템 타이머 확인은 Windows가 두 가지 주요 작업을 수행하는 빈도를 결정합니다.
- 전체 틱이 경과 한 경우 타이머 틱 수를 업데이트합니다.
- 예약 된 타이머 개체가 만료되었는지 확인합니다.
타이머 틱은 Windows가 하루 중 시간 및 스레드 퀀텀 시간을 추적하는 데 사용하는 경과 시간의 개념입니다. 기본적으로 클럭 인터럽트와 타이머 틱은 동일하지만 Windows 또는 응용 프로그램에서 클럭 인터럽트 기간을 변경할 수 있습니다.
Windows 7의 기본 타이머 해상도는 15.6 밀리 초 (ms)입니다. 일부 애플리케이션에서는이를 1ms로 줄여 모바일 시스템의 배터리 사용 시간을 25 %까지 줄입니다.
원래 출처 : Timers, Timer Resolution, Development of Efficient Code (docx).
The timer resolution is given by the system heartbeat. This typically defaults to 64 beats/s which is 15.625 ms. However there are ways to modify these system wide settings to achieve timer resolutions down to 1 ms or even to 0.5 ms on newer platforms:
1. Going for 1 ms resolution by means of the multimedia timer interface:
The multimedia timer interface is able to provide down to 1 ms resolution. See About Multimedia Timers (MSDN), Obtaining and Setting Timer Resolution (MSDN), and this answer for more details about timeBeginPeriod
. Note: Don't forget to call the timeEndPeriod to switch back to the default timer resolution when done.
How to do:
#define TARGET_RESOLUTION 1 // 1-millisecond target resolution
TIMECAPS tc;
UINT wTimerRes;
if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR)
{
// Error; application can't continue.
}
wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
timeBeginPeriod(wTimerRes);
// do your stuff here at approx. 1 ms timer resolution
timeEndPeriod(wTimerRes);
Note: This procedure is availble to other processes as well and the obtained resolution applies system wide. The highest resoltion requested by any process will be active, mind the consequences.
2. Going to 0.5 ms resolution:
You may obtain 0.5 ms resolution by means of the hidden API NtSetTimerResolution()
. NtSetTimerResolution is exported by the native Windows NT library NTDLL.DLL. See How to set timer resolution to 0.5ms ? on MSDN. Nevertheless, the true achievable resoltion is determined by the underlying hardware. Modern hardware does support 0.5 ms resolution. Even more details are found in Inside Windows NT High Resolution Timers. The supported resolutions can be obtained by a call to NtQueryTimerResolution().
How to do:
#define STATUS_SUCCESS 0
#define STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000245
// after loading NtSetTimerResolution from ntdll.dll:
// The requested resolution in 100 ns units:
ULONG DesiredResolution = 5000;
// Note: The supported resolutions can be obtained by a call to NtQueryTimerResolution()
ULONG CurrentResolution = 0;
// 1. Requesting a higher resolution
// Note: This call is similar to timeBeginPeriod.
// However, it to to specify the resolution in 100 ns units.
if (NtSetTimerResolution(DesiredResolution ,TRUE,&CurrentResolution) != STATUS_SUCCESS) {
// The call has failed
}
printf("CurrentResolution [100 ns units]: %d\n",CurrentResolution);
// this will show 5000 on more modern platforms (0.5ms!)
// do your stuff here at 0.5 ms timer resolution
// 2. Releasing the requested resolution
// Note: This call is similar to timeEndPeriod
switch (NtSetTimerResolution(DesiredResolution ,FALSE,&CurrentResolution) {
case STATUS_SUCCESS:
printf("The current resolution has returned to %d [100 ns units]\n",CurrentResolution);
break;
case STATUS_TIMER_RESOLUTION_NOT_SET:
printf("The requested resolution was not set\n");
// the resolution can only return to a previous value by means of FALSE
// when the current resolution was set by this application
break;
default:
// The call has failed
}
Note: The functionality of NtSetTImerResolution is basically mapped to the functions timeBeginPeriod
and timeEndPeriod
by using the bool value Set
(see Inside Windows NT High Resolution Timers for more details about the scheme and all its implications). However, the multimedia suite limits the granularity to milliseconds and NtSetTimerResolution allows to set sub-millisecond values.
All replays here are about system timer resolution. But .net timers not respect it. As author notice by himself:
that the runtime implements its own timer queue that is similar to the Timer Queue Timers, but never actually calls the Win32 functions.
And Jan pointed in comment.
So, answers above are good info, but not directly correlated to .net timers and therefore misleading people :(
Short answer to both author questions is by design. Why did they decide to go this way? Feared about whole system performance? Who knows...
To not duplicate, see more info on both questions (and ways to implement precise timers on .net) in Jan's correlated topic.
참고URL : https://stackoverflow.com/questions/3744032/why-are-net-timers-limited-to-15-ms-resolution
'Development Tip' 카테고리의 다른 글
Kotlin 코 루틴이 RxKotlin보다 얼마나 나은가요? (0) | 2020.12.06 |
---|---|
python 및 sys.argv (0) | 2020.12.06 |
Visual Studio없이 Visual C ++ 명령 줄 컴파일러를 다운로드 할 수 있습니까? (0) | 2020.12.05 |
호스팅 된 TeamCity 빌드 공급자를 아는 사람이 있습니까? (0) | 2020.12.05 |
iOS Swift에서 원격 JSON 데이터를 로컬 캐시 저장소에 동기화 (0) | 2020.12.05 |