2015-03-16 4 views
4

У нас есть журналы приложений, которые записывают информацию о производительности для различных дорогостоящих операций. Мы используем как Stopwatch, так и DateTime.UtcNow в нашем протоколировании, и мы находим, что эти значения могут отличаться МНОГО больше, чем можно было бы ожидать, даже при условии точности DateTime.UtcNow ~ 20 мс. Мой вопрос: Что может быть причиной этого и может быть исправлено?Секундомер и DateTime.UtcNow, производящие неожиданно большие временные изменения

Информация записанная:

  • время_запуска (DateTime.UtcNow)
  • Продолжительность (TimeSpan.FromSeconds((after - before)/(double)Stopwatch.Frequency), где after и before являются значения Stopwatch.GetTimestamp() в начале и в конце операции
  • EndTime (DateTime.UtcNow)

Вы ожидали бы, что EndTime будет близок к StartTime + Duration, но в некоторых случаях это далеко от. Мы опробовали 10000 таких измерений, ища случаи, когда EndTime и (StartTime + Duration) отличались более чем на 20 мс. Мы обнаружили следующее:

  • ~ 2% записей выключены на> 20мс
  • Из них, они выключены в среднем 100мс (макс является 1.4seconds)
  • Из них, в некоторых случаях секундомер рассчитанных конечное времени больше, чем DateTime на основе конечного времени, но в большинстве случаев секундомера время меньше

машина Информация

  • Windows Server 2012 R2 Hyper-V VM работает поверх Windows Server 2012 R2
  • 32GB барана для VM, 8 виртуальных процессоров
+1

Вот часть [статьи] (http://tech.pro/tutorial/1295/c-performance-benchmark-mistakes-part-two) Эрика Липперта о том, почему секундомер лучше, чем вызовы 'DateTime.Now 'для определения того, сколько времени прошло в программе. – juharr

+0

@Chase Могу ли я спросить вас о длительности измеренных операций? Секунды? Минуты? Часы? – xanatos

+0

Взгляните на https://msdn.microsoft.com/en-us/library/system.datetime.utcnow%28v=vs.110%29.aspx, похоже, что utcnow имеет приблизительное разрешение около 10 миллисекунд, поэтому что может объяснить некоторые различия, которые вы наблюдаете. –

ответ

6

Это следовало ожидать. StopWatch использует QueryPerformanceCounter (QPC) под капотом, в то время как DateTime.UtcNow использует часы реального времени компьютера (RTC).

  • QPC происходит от временной метки счетчика вашего процессора (TSC), и является очень точным , но не имеет никаких оснований для точности с относительно UTC.
  • RTC находится на материнской плате компьютера и обычно строится на дешевых кварцевых генераторах. Это точное, но не точное.

Для измерения прошедшего времени вы хотите получить точность и, следовательно, должны использовать Stopwatch.

Чтобы получить текущее время суток, вы хотите получить точность, и поэтому должны использовать DateTime.UtcNow.

Отличный читатель с большим количеством вспомогательных деталей can be found here on MSDN.

Изменения, которые вы измерили, вызваны дрейфом часов в RTC. Ваш RTC будет (сам по себе) падать на несколько миллисекунд впереди или позади реального времени. Это довольно часто и периодически исправляется с помощью синхронизации NTP. Для незначительных корректировок ОС будет распространять исправление в меньших интервалах, несколько миллисекунд за раз, в течение нескольких секунд.

Насколько обходной путь идет, вы могли бы рассмотреть класс как это:

public static class PreciseClock 
{ 
    private static readonly DateTime StartTime = DateTime.UtcNow; 
    private static readonly Stopwatch Stopwatch = Stopwatch.StartNew(); 

    public static DateTime GetCurrentUtcTime() 
    { 
     return StartTime + Stopwatch.Elapsed; 
    } 
} 

Однако недостатком является то, что вы делаете предположение, что RTC находится в идеальной синхронизации при инициализации класса. На самом деле у вас нет никакой гарантии.

Если вы хотите некоторые уровня гарантии, вы можете рассмотреть возможность сделать вызов NTP себя. Я реализовал класс, который делает именно это в NodaTime.NetworkClock. Он реализует интерфейс IClock от Noda Time. Он периодически делает вызов на сервер NTP и отслеживает время между вызовами, используя Stopwatch. Вы бы использовать его как это:

// grab the clock's singleton instance 
var clock = NetworkClock.Instance; 

// optionally set the ntp server during your app's startup 
clock.NtpServer = "pool.ntp.org"; // or whatever server you want to sync with 

// Get the current utc time whenever you like 
DateTime utcNow = clock.Now.ToDateTimeUtc(); 

Кроме того, есть такая вещь, как «Multimedia Timer», или «High Precision Event Timer» (HPET), который доступен на большинстве современных аппаратных средств. Он предлагает еще большую точность, чем QPC. Однако нет прямого класса, который предоставляет это в .NET Framework. При поиске вы найдете несколько реализаций, которые обертывают функции Win32, которые его предоставляют. Обычно это излишне, если вы не делаете такие вещи, как графика или звук.

+0

Я могу купить, что они будут отключены на несколько мс, но я вижу сотни мс. Будет ли то, что вы описываете, действительно объясняет эту большую разницу? – ChaseMedallion

+0

Конечно, это может быть легко или даже больше. Все зависит от качества кристалла RTC на материнской плате. Некоторые аппаратные средства лучше других. На моей коробке я измерил поправки от 10 до 60 мс, но нет причин, чтобы они не могли быть больше на другом оборудовании. Кроме того, вы сказали, что запускаете это на виртуальной машине, и это вполне возможно, что влияет на результаты. Я не совсем уверен в механике виртуализации RTC, но я уверен, что хост-ОС должен будет передать значение RTC гостю, и там может быть латентность. –

+0

Возможно, вы захотите запустить те же тесты непосредственно на серверном оборудовании и посмотреть, не изменилось ли это. –

 Смежные вопросы

  • Нет связанных вопросов^_^