Являются ли таймеры и петли в .Net точными?
При разработке программы для расчета частоты и ширины импульса импульса, генерируемого таймером 555 таймера, поступающего на ПК через параллельный порт ПК. Я заметил, что каждый раз, когда я запускаю код, он показывает разные значения, поэтому я начинаю тестировать циклы и таймеры на точность. Я запустил следующий код и пришел к выводу, что они неточны (возможно, я ошибаюсь, пожалуйста, поправьте меня, если я есть!):
Для таймеров:
int sec = 0;
private void button2_Click(object sender, EventArgs e)
{
sec = DateTime.Now.Second;
i = 0;
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
if (sec == DateTime.Now.Second)
{
i++;
}
else
{
timer1.Enabled = false;
MessageBox.Show(i.ToString(),"Timer Output");
}
}
ВЫВОД:
Должно быть одинаковым, но:
![enter image description here]()
![enter image description here]()
![enter image description here]()
Для LOOP:
private void button1_Click(object sender, EventArgs e)
{
i = 0;
CheckForIllegalCrossThreadCalls = false;
Thread t1 = new Thread(LoopTest);
t1.Start();
}
void LoopTest()
{
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
this.Cursor = Cursors.WaitCursor;
while (true)
{
if (sw.ElapsedMilliseconds != 1000)
{
i++;
}
else
{
break;
}
}
sw.Stop();
this.Cursor = Cursors.Default;
MessageBox.Show(i.ToString(), "Loop Output");
}
ВЫВОД:
Должно быть одинаковым, но:
![enter image description here]()
![enter image description here]()
![enter image description here]()
Что делать, чтобы сделать петли и таймер точным, есть ли способ сделать это? Или мне нужно перейти к сложному и сложному C-коду и DOS?
Я думаю, что это основная причина для получения неправильных значений в этом вопросе: Количество входных частот параллельного порта - С#
Ответы
Ответ 1
1) Не используйте DateTime.Now
для измерения производительности, используйте StopWatch
.
2) "OUTPUT: Should be same, but ..
"
Почему они должны? Вы используете управляемый/JIT-код в не RTOS (операционная система реального времени). Вы можете получить код в любое время, если ОС почувствует это. Что побуждает вас полагать, что выполнение одного и того же кода N раз в этой среде должно всегда давать такие же результаты в такой небольшой степени?
3) Таймеры в Windows имеют разрешение ~ 15 мс. Лучшая ставка для очень точных таймингов - это API HighPerformanceTimer для систем (ЦП), которые его поддерживают. Вы даже не указали нам интервал таймера.
Вы не можете рассматривать множество переменных здесь, и вы основываете свои прогнозы на ошибочных предположениях. Сколько раз вы даже измеряете этот код? Учитываете ли вы время, необходимое для его компиляции в первый раз? Вы работаете в режиме выпуска? Через VS? Много ли работает в фоновом режиме? Я мог бы продолжить.
Ответ 2
Тщательная реализация позволяет измерять периоды времени на большинстве платформ Windows с точностью до нескольких микросекунд.
Обратите внимание на следующие факты:
-
Windows не является оперативной ОС: здесь это неважно!
-
Использовать приоритеты процесса/потока: SetPriorityClass до REALTIME_PRIORITY_CLASS
и SetThreadPriority до THREAD_PRIORITY_TIME_CRITICAL
. Убедитесь, что у вас есть безопасный код, потому что эти приоритеты могут блокировать систему, пока вызывающий поток занят.
(Process.PriorityClass и Thread.Priority не способны повысить приоритеты до требуемых уровней.)
-
Возможно, увеличьте интервал прерывания и интервал обновления системы с помощью Мультимедийный таймер. (Существуют различные .NET проекты, которые обертывают эти функции мультимедийного таймера.)
-
В многоядерных системах выбор ядра может также влиять на точность. Преимущественно заставить поток ждать ожидания таймера на процессоре0. Функция SetThreadAffinityMask позволяет привязать поток к определенному процессору. (Для приложений .Net см. Thread.ProcessorAffinity.)
-
Используйте QueryPerformanceCounter и QueryPerformanceFrequency как источники для высокочастотных временных измерений. (Для приложений .Net см.: Создание класса Weryper QueryPerfCounter)
-
Убедитесь, что значение частоты счетчиков производительности откалибровано. Значение, возвращаемое QueryPerformanceFrequency
, отличается от наблюдаемого значения смещением и некоторым тепловым дрейфом. Это может/приведет к ошибкам многих пользователей. См. Проект Windows Timestamp, чтобы узнать, как такая калибровка может быть выполнена.
И: Да, вам, возможно, придется пойти на жесткую кодировку. Но можно наблюдать микросекундную синхронизацию
очень надежна на платформах Windows.
Примечание. Windows не является операционной системой реального времени. Но таймеры, доступные в окнах, очень точны. Они делают именно то, что они должны делать, и делают это с очень высокой точностью. Тот факт, что есть много жалоб на таймеры окон и их точность, заключается в том, что их поведение сильно зависит от основного оборудования. Это также является причиной того, что документация имеет много недостатков. Настоятельно рекомендуется диагностировать оборудование, чтобы найти индивидуальные возможности обслуживания времени. К сожалению, это приводит к тому, что любая программа имеет некоторые дополнительные строки кода, которые не зависят от платформы.
Ответ 3
Прежде всего, вы не знаете, как далеко в текущую секунду происходит время, когда вы вызываете button2_click. Таким образом, это в основном случайное количество оставшейся секунды, когда отображается MessageBox - это то, что вы видите.
Во-вторых, сколько циклов ЦП, которые цикл получает в течение промежутка времени, не имеет ничего общего с точностью.
Сколько циклов, которые получает любой поток, зависит от того, что еще происходит в системе. Если бы система решила, что целая куча циклов необходима для перехода к другому процессу, ваш поток будет "голоден" в течение некоторого времени.
Возможно, вы можете подробно рассказать о том, что вы действительно пытаетесь сделать, и кто-то может предложить некоторые советы.
Ответ 4
Ответ на ответ правильный: таймеры в Windows не точны. Ну, конечно, недостаточно точно для измерения аппаратных сигналов.
Но, если бы мне пришлось измерять частоту и ширину импульса, я бы взял сотню проб или около того и усвоил их. Возможно, так:
private StopWatch _Sw = new StopWatch();
private List<TimeSpan> _Samples = new List<TimeSpan>();
private Timer _Timer = new Timer(TimerTick, TimeSpan.FromSeconds(1));
private const int RequiredSamples = 100;
private void StartSampling()
{
// You can change this next line to PriorityClass.RealTime if you're careful.
System.Diagnostics.Process.GetCurrentProcess().BasePriority
= PriorityClass.High;
_Samples.Capacity = RequiredSamples;
_Timer.Start();
_Sw.Start();
Hook555Timer(On555Pulse);
}
private void On555Pulse(object sender, EventArgs e)
{
_Sample.Add(_Sw.Elapsed);
}
private void TimerTick(object sender, EventArgs e)
{
if (_Samples.Count > RequiredSamples)
{
System.Diagnostics.Process.GetCurrentProcess().BasePriority
= PriorityClass.Normal;
_Timer.Stop();
_Sw.Stop();
UnHook555Timer(On555Pulse);
// You can now use the time between each TimeSpan
// in _Samples to determine statistics about your timer.
// Eg: Min / Max duration, average and median duration.
var durations = _Samples
.Zip(_Samples.Skip(1),
(a,b) => new { First = a, Second = b } )
.Select(pair => pair.Second.Subtract(pair.First));
var minTime = durations.Min(ts => ts.TotalMilliseconds);
var maxTime = durations.Max(ts => ts.TotalMilliseconds);
var averageTime = durations.Average(ts => ts.TotalMilliseconds);
// I don't think LINQ has a Median() aggregate out of the box.
// Some comment about "an exercise for the reader" goes here.
var medianTime = durations.Median(ts => ts.TotalMilliseconds);
var frequency = _Samples.Last()
.Subtract(_Samples.First())
.TotalSeconds / _Samples.Count;
}
}
(NB: код, написанный в блокноте, вряд ли будет работать без дополнительных изменений)
Моя точность теперь определяется StopWatch
, а не Timer
(обратите внимание, что StopWatch
может быть не более точной, чем Timer
в вашей системе, проверьте свойства Frequency
и IsHighResolution
)., И я увеличиваю приоритет процесса во время выборки, чтобы минимизировать другие процессы, предотвращающие мой процесс.
Но даже в этом случае, поскольку Windows не является RTOS, лучше всего вы можете сделать много образцов и использовать некоторые статистические данные, чтобы получить приближение ответа.