Почему System.nanoTime () и System.currentTimeMillis () так быстро расходятся? - PullRequest
20 голосов
/ 30 апреля 2011

В целях диагностики я хочу иметь возможность обнаруживать изменения в системных часах времени в долго работающем серверном приложении.Поскольку System.currentTimeMillis() основано на времени настенных часов, а System.nanoTime() основано на системном таймере, который не зависит (*) от времени настенных часов, я подумал, что могу использовать изменения разности между этими значениями для обнаружения изменений системного времени.

Я написал приложение для быстрого тестирования, чтобы увидеть, насколько стабильна разница между этими значениями, и, к моему удивлению, значения сразу меняются на уровне нескольких миллисекунд в секунду.Несколько раз я видел гораздо более быстрые расхождения.Это на 64-битном рабочем столе Win7 с Java 6. Я не пробовал эту тестовую программу ниже под Linux (или Solaris или MacOS), чтобы увидеть, как она работает.Для некоторых прогонов этого приложения расхождение положительное, для некоторых прогонов оно отрицательное.Похоже, это зависит от того, что еще делает рабочий стол, но трудно сказать.

public class TimeTest {
  private static final int ONE_MILLION  = 1000000;
  private static final int HALF_MILLION =  499999;

  public static void main(String[] args) {
    long start = System.nanoTime();
    long base = System.currentTimeMillis() - (start / ONE_MILLION);

    while (true) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // Don't care if we're interrupted
      }
      long now = System.nanoTime();
      long drift = System.currentTimeMillis() - (now / ONE_MILLION) - base;
      long interval = (now - start + HALF_MILLION) / ONE_MILLION;
      System.out.println("Clock drift " + drift + " ms after " + interval
                         + " ms = " + (drift * 1000 / interval) + " ms/s");
    }
  }
}

Неточности со временем Thread.sleep(), а также прерывания должны быть совершенно не связаны с дрейфом таймера.

Оба этих «системных» вызова Java предназначены для использования в качестве измерения - один для измерения разницы во времени настенных часов, а другой - для измерения абсолютных интервалов, поэтому, когда часы реального времени не меняются,эти значения должны меняться очень близко к той же скорости, верно?Это ошибка или слабость или сбой в Java?Есть ли в ОС или оборудовании что-то, что мешает Java быть более точным?

Я полностью ожидаю некоторого дрейфа и дрожания (**) между этими независимыми измерениями, но я ожидал гораздо меньше минуты в день дрейфа.,1 мс в секунду дрейфа, если он монотонный, это почти 90 секунд!Мой наблюдаемый дрейф в худшем случае был, возможно, в десять раз большеКаждый раз, когда я запускаю эту программу, я вижу дрейф в самом первом измерении.До сих пор я не запускал программу более 30 минут.

Я ожидаю увидеть небольшую случайность в напечатанных значениях из-за дрожания, но почти во всех запусках программы я вижу устойчивый ростразницы, часто до 3 мсек в секунду при увеличении и в пару раз гораздо больше.

Есть ли в любой версии Windows механизм, аналогичный Linux, который регулирует тактовую частоту системы, чтобы медленно приводитьвремя суток синхронизируется с внешним источником часов?Повлияет ли такая вещь на оба таймера или только на таймер настенных часов?

(*) Я понимаю, что на некоторых архитектурах System.nanoTime() будет по необходимости использовать тот же механизм, что и System.currentTimeMillis().Я также считаю, что справедливо предположить, что любой современный сервер Windows не имеет такой аппаратной архитектуры.Это неверное предположение?

(**) Конечно, System.currentTimeMillis() обычно имеет гораздо больший джиттер, чем System.nanoTime(), поскольку его гранулярность не составляет 1 мсек в большинстве систем.

Ответы [ 5 ]

11 голосов
/ 30 апреля 2011

Вы можете найти этот пост в блоге Sun / Oracle о таймерах JVM , который может быть интересен.

Вот пара абзацев из этой статьи о таймерах JVM под Windows:

System.currentTimeMillis() реализован с использованием метода GetSystemTimeAsFileTime, который, по сути, просто считывает значение времени суток низкого разрешения, которое поддерживает Windows. Чтение этой глобальной переменной, естественно, происходит очень быстро - около 6 циклов согласно сообщенной информации. Это значение времени дня обновляется с постоянной скоростью, независимо от того, как было запрограммировано прерывание по таймеру - в зависимости от платформы это будет либо 10 мс, либо 15 мс (это значение кажется привязанным к периоду прерывания по умолчанию).

System.nanoTime() реализован с использованием API QueryPerformanceCounter / QueryPerformanceFrequency (если доступно, в противном случае возвращается currentTimeMillis*10^6). QueryPerformanceCounter (QPC) реализован по-разному в зависимости от оборудования, на котором он работает. Обычно он использует либо программируемый интервальный таймер (PIT), либо таймер управления питанием ACPI (PMT), либо счетчик меток времени на уровне ЦП (TSC). Доступ к PIT / PMT требует выполнения медленных команд порта ввода / вывода, и в результате время выполнения для QPC составляет порядка микросекунд. В отличие от этого, чтение TSC составляет порядка 100 тактов (для чтения TSC из микросхемы и преобразования его во временное значение, основанное на рабочей частоте). Вы можете определить, использует ли ваша система ACPI PMT, проверив, возвращает ли QueryPerformanceFrequency значение подписи 3579545 (т.е. 3,57 МГц). Если вы видите значение около 1,19 МГц, то ваша система использует старый чип PIT 8245. В противном случае вы должны увидеть значение, приблизительно равное частоте вашего ЦП (по модулю любой регулировки скорости или управления питанием, которые могут быть в силе.)

7 голосов
/ 24 октября 2013

Я не уверен, насколько это на самом деле поможет.Но это область активных изменений в мире Windows / Intel / AMD / Java.Необходимость точного и точного измерения времени была очевидна в течение нескольких (как минимум 10) лет.И Intel, и AMD ответили, изменив работу TSC.Обе компании теперь имеют то, что называется Invariant-TSC и / или Constant-TSC .

Проверьте точность rdtsc для всех ядер ЦП .Цитирование из osgx (который ссылается на руководство Intel).

"16.11.1 Инвариантный TSC

Счетчик меток времени в более новых процессорах может поддерживать расширение, называемое инвариантным TSC. Поддержка процессорадля инварианта TSC указывается PUID.80000007H: EDX [8].

Инвариант TSC будет работать с постоянной скоростью во всех состояниях ACPI P-, C- и T. Это перемещение архитектурывперед. На процессорах с инвариантной поддержкой TSC ОС может использовать TSC для служб таймера настенных часов (вместо таймеров ACPI или HPET). Чтения TSC гораздо более эффективны и не требуют дополнительных затрат, связанных с переходом кольца или доступом кресурс платформы. "

См. также http://www.citihub.com/requesting-timestamp-in-applications/. Цитирование от автора

Для AMD:

Если CPUID 8000_0007.edx [8] = 1, тоСкорость TSC гарантируется как инвариантная для всех P-состояний, C-состояний и переходов с остановкой-предоставлением (таких как регулирование STPCLK);поэтому TSC подходит для использования в качестве источника времени.

Для Intel:

Поддержка процессором инвариантного TSC указывается в CPUID.80000007H: EDX [8].Инвариантный TSC будет работать с постоянной скоростью во всех ACPI P-, C-.и Т-состояния.Это архитектурное поведение, движущееся вперед.На процессорах с инвариантной поддержкой TSC ОС может использовать TSC для служб таймера настенных часов (вместо таймеров ACPI или HPET).Чтения TSC намного более эффективны и не требуют дополнительных затрат, связанных с переходом по кольцу или доступом к ресурсу платформы. "

Теперь действительно важным моментом является то, что новейшие JVM, похоже, используют новые надежные механизмы TSC.В интернете не так много, чтобы показать это. Однако взгляните на http://code.google.com/p/disruptor/wiki/PerformanceResults.

". Чтобы измерить задержку, мы используем трехэтапный конвейер и генерируем события с меньшим, чем насыщение.Это достигается путем ожидания 1 микросекунды после введения события, а затем повторения 50 миллионов раз.Для определения времени на этом уровне точности необходимо использовать счетчики меток времени от ЦП.Мы выбираем ЦП с инвариантным TSC, потому что старые процессоры страдают от изменения частоты из-за энергосбережения и состояния сна.Процессоры Intel Nehalem и более поздние версии используют инвариантный TSC, к которому могут обращаться последние JVM Oracle, работающие в Ubuntu 11.04.Для этого теста привязка ЦП не использовалась "

Обратите внимание, что авторы" Disruptor "тесно связаны с людьми, работающими над Azul и другими JVM.

См. Также" Java Flight«Записи за кулисами». В этой презентации упоминаются новые инвариантные инструкции TSC.

2 голосов
/ 02 декабря 2014

Есть ли в какой-либо версии Windows механизм, аналогичный Linux, который регулирует частоту системных часов, чтобы медленно синхронизировать часы времени с внешним источником часов? Повлияет ли это на оба таймера или только на настенные часы?

Проект отметки времени Windows делает то, что вы просите. Насколько я знаю, это влияет только на настенный таймер.

2 голосов
/ 22 июля 2012

System.currentTimeMillis() и System.nanoTime() не обязательно предоставляются одним и тем же оборудованием.System.currentTimeMillis(), поддерживаемый GetSystemTimeAsFileTime(), имеет элементы разрешения 100 нс.Его источником является системный таймер.System.nanoTime() поддерживается высокопроизводительным счетчиком системы.Существует целый ряд различного оборудования, обеспечивающего этот счетчик.Поэтому его разрешение меняется в зависимости от используемого оборудования.

Ни в коем случае нельзя предполагать, что эти два источника находятся в фазе.Измерение двух значений друг против друга покажет разную скорость движения.Если обновление System.currentTimeMillis() принимается за реальный прогресс во времени, вывод System.nanoTime() может иногда быть медленнее, иногда быстрее, а также изменяться.

Необходимо выполнить тщательную калибровку, чтобы синхронизировать фазы этих двух источников времени.

Более подробное описание взаимосвязи между этими двумя источниками времени можно найти в Windows.Метка времени проекта .

2 голосов
/ 30 апреля 2011

"Возвращает текущее значение наиболее точного доступного системного таймера в наносекундах.

" Этот метод может использоваться только для измерения прошедшего времени и не связан с какими-либо другими понятиями системы или настенных часоввремя.Возвращаемое значение представляет наносекунды с некоторого фиксированного, но произвольного времени (возможно, в будущем, поэтому значения могут быть отрицательными).Этот метод обеспечивает точность наносекунды, но не обязательно точность наносекунды.Нет никаких гарантий относительно того, как часто меняются значения.Различия в последовательных вызовах, которые охватывают более чем приблизительно 292 года (2 ** 63 наносекунды), не будут точно вычислять истекшее время из-за переполнения чисел.1005 *

Это не «ошибка в Java» или «ошибка» в чем-либо. Это определение. Разработчики JVM оглядываются, чтобы найти самые быстрые часы / таймер в системе и использовать их. Если это на этапе блокировкис системными часами, тогда хорошо, но если это не так, то печенье просто рушится. Скажем, вполне вероятно, что компьютерная система будет иметь точные системные часы, но затем будет иметь более высокоскоростной таймер, привязанный к процессору.тактовая частота или что-то подобное. Поскольку тактовая частота часто изменяется, чтобы минимизировать энергопотребление, скорость приращения этого внутреннего таймера будет варьироваться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...