Неужели System.nanoTime() полностью бесполезен?

Как задокументировано в блоге Beware для System.nanoTime() в Java, в системах x86 Java System.nanoTime() возвращает значение времени с помощью счетчика, специфичного для процессора. Теперь рассмотрим следующий случай, который я использую для измерения времени звонка:

long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;

Теперь в многоядерной системе может быть так, что после измерения времени1 поток планируется на другой процессор, чей счетчик меньше, чем у предыдущего процессора. Таким образом, мы можем получить значение в time2, которое меньше time1. Таким образом мы бы получили отрицательное значение в timeSpent.

Рассматривая этот случай, не правда ли, что System.nanotime пока бесполезен?

Я знаю, что изменение системного времени не влияет на nanotime. Это не проблема, которую я описал выше. Проблема в том, что каждый ЦП будет держать свой счетчик, так как он был включен. Этот счетчик может быть ниже на втором процессоре по сравнению с первым процессором. Поскольку поток может быть запланирован операционной системой на второй процессор после получения time1, значение timeSpent может быть неправильным и даже отрицательным.

Ответ 1

Это сообщение неверно, и nanoTime безопасен. Там есть комментарий к сообщению, которое ссылается на сообщение в блоге Дэвида Холмса, парня в реальном времени и парня по параллелизму в Sun. Это говорит:

System.nanoTime() реализован с использованием API QueryPerformanceCounter/QueryPerformanceFrequency [...] Механизм по умолчанию, используемый QPC, определяется уровнем аппаратной абстракции (HAL) [...]. Это значение по умолчанию изменяется не только на оборудовании, но и на всей ОС версии. Например, пакет обновления 2 (SP2) для Windows XP изменил использование таймера управления питанием (PMTimer) вместо счетчика меток времени процессора (TSC) из-за проблем с синхронизацией TSC на разных процессорах в системах SMP и из-за его частоты может варьироваться (и, следовательно, его отношение к прошедшему времени) в зависимости от настроек управления питанием.

Итак, в Windows это было проблемой до WinXP SP2, но не сейчас.

Я не могу найти часть II (или больше), в которой говорится о других платформах, но в этой статье есть замечание, что Linux столкнулся и решил ту же проблему аналогичным образом, со ссылкой на FAQ для clock_gettime (CLOCK_REALTIME), который говорит:

  1. Является ли clock_gettime (CLOCK_REALTIME) одинаковым для всех процессоров/ядер? (Имеет ли значение arch? Например, ppc, arm, x86, amd64, sparc).

Это должно или это считается глючным.

Однако в x86/x86_64 можно увидеть несинхронизированные или переменные TSC частоты, вызывающие несоответствия во времени. Ядра 2.4 действительно не имели защиты от этого, и ранние ядра 2.6 тоже не очень хорошо справлялись. Начиная с версии 2.6.18 логика обнаружения этого лучше, и мы обычно возвращаемся к безопасному источнику часов.

PPC всегда имеет синхронизированную временную базу, так что это не должно быть проблемой.

Таким образом, если ссылка Holmes может быть прочитана как подразумевающая, что nanoTime вызывает clock_gettime(CLOCK_REALTIME), то она безопасна с ядра 2.6.18 на x86 и всегда на PowerPC (потому что IBM и Motorola, в отличие от Intel, фактически знают, как проектировать микропроцессоры).

К сожалению, нет никаких упоминаний о SPARC или Solaris. И, конечно, мы понятия не имеем, что делают IBM JVM. Но Sun JVM на современных Windows и Linux понимают это правильно.

РЕДАКТИРОВАТЬ: Этот ответ основан на источниках, которые он цитирует. Но я все еще волнуюсь, что это может быть совершенно неправильно. Еще одна актуальная информация была бы действительно ценной. Я только что наткнулся на ссылку на четырехлетнюю новую статью о часах Linux, которая может быть полезна.

Ответ 2

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

Отметьте эту цитату на сайте Java Sun:

Часы реального времени и System.nanoTime() основаны на тот же системный вызов и, следовательно, тот же часы.

С Java RTS все временные API (например, Таймеры, Периодические Темы, мониторинг сроков и т.д. ) основаны на таймер с высоким разрешением. И вместе с приоритетами в реальном времени, они могут убедитесь, что соответствующий код будет выполняться в нужное время для в режиме реального времени. В противоположность, обычные API Java SE предлагают только несколько методы, способные обрабатывать с высоким разрешением, без гарантия исполнения при заданном время. Использование System.nanoTime() между различные моменты в коде для выполнения прошедшие измерения времени должны всегда быть точным.

Java также имеет оговорку для метода nanoTime():

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

Казалось бы, единственный вывод, который можно сделать, это то, что nanoTime() нельзя полагаться как точное значение. Таким образом, если вам не нужно измерять время, которое составляет всего несколько секунд, то этот метод достаточно хорош, даже если возвращаемое значение отрицательно. Однако, если вам нужна более высокая точность, они, похоже, рекомендуют использовать JAVA RTS.

Итак, чтобы ответить на ваш вопрос... nanoTime() не бесполезен... его просто не самый разумный метод для использования в любой ситуации.

Ответ 3

Не нужно обсуждать, просто используйте источник. Здесь SE 6 для Linux сделайте свои выводы:

jlong os::javaTimeMillis() {
  timeval time;
  int status = gettimeofday(&time, NULL);
  assert(status != -1, "linux error");
  return jlong(time.tv_sec) * 1000  +  jlong(time.tv_usec / 1000);
}


jlong os::javaTimeNanos() {
  if (Linux::supports_monotonic_clock()) {
    struct timespec tp;
    int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp);
    assert(status == 0, "gettime error");
    jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec);
    return result;
  } else {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "linux error");
    jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec);
    return 1000 * usecs;
  }
}

Ответ 4

Отказ от ответственности: я являюсь разработчиком этой библиотеки

Вам может понравиться это лучше:

http://juliusdavies.ca/nanotime/

Но он копирует файл DLL или Unix.so(shared object) в домашний каталог текущего пользователя, чтобы он мог вызвать JNI.

Некоторая справочная информация находится на моем сайте по адресу:

http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html

Ответ 5

Linux исправляет несоответствия между процессорами, но Windows этого не делает. Я предлагаю вам предположить, что System.nanoTime() имеет точность всего около 1 микросекунды. Простым способом получения более длительного времени является вызов foo() 1000 или более раз и деление времени на 1000.

Ответ 6

Абсолютно не бесполезно. Сторонники времени правильно указывают на многоядерную проблему, но в приложениях реального времени она часто радикально лучше, чем currentTimeMillis().

При вычислении графических позиций в обновлении кадра nanoTime() приводит к MULCH более плавному движению в моей программе.

И я тестирую только на многоядерных машинах.

Ответ 7

Я видел отрицательное время, прошедшее с использованием System.nanoTime(). Чтобы быть ясным, данный код:

    long startNanos = System.nanoTime();

    Object returnValue = joinPoint.proceed();

    long elapsedNanos = System.nanoTime() - startNanos;

и переменная 'elapsedNanos' имела отрицательное значение. (Я уверен, что промежуточный вызов занял менее 293 лет, что является точкой переполнения для наносов, хранящихся в longs:)

Это произошло с использованием IBM v1.5 JRE 64bit на IBM P690 (многоядерном) оборудовании под управлением AIX. Я только видел, что эта ошибка возникает один раз, поэтому она кажется крайне редкой. Я не знаю причину - это аппаратно-специфическая проблема, дефект JVM - я не знаю. Я также не знаю последствий для точности nanoTime() в целом.

Чтобы ответить на исходный вопрос, я не думаю, что nanoTime бесполезен - он обеспечивает субмиллисекундный тайм-аут, но существует реальный (а не только теоретический) риск его неточности, который вам нужно учитывать.

Ответ 8

Нет, это не так... Это зависит только от вашего процессора, проверьте Высокий таймер событий, как/почему все по-другому обрабатывается в соответствии с процессором.

В принципе, прочитайте источник своей Java и проверьте, что делает ваша версия с этой функцией, и если она работает против процессора, вы будете запускать ее.

IBM даже предлагает, которую вы используете для тестирования производительности (сообщение 2008 года, но обновлено).

Ответ 9

Это не похоже на проблему с Core 2 Duo под управлением Windows XP и JRE 1.5.0_06.

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

[EDIT] Я бы предположил, что это происходит только на физически отдельных процессорах, т.е. что счетчики синхронизируются для нескольких ядер на одной и той же матрице.

Ответ 10

Я связываюсь с тем, что по сути является тем же самым обсуждением, где Питер Лори дает хороший ответ. Почему я получаю минус прошедшее время с помощью System.nanoTime()?

Многие люди упомянули, что в Java System.nanoTime() может вернуться отрицательное время. Я извиняюсь за повторение того, что уже говорили другие люди.

  • nanoTime() не является часовым, а счетчиком циклов процессора.
  • Возвращаемое значение делится на частоту, чтобы выглядеть как время.
  • Частота процессора может колебаться.
  • Когда ваш поток запланирован на другом CPU, есть вероятность получить nanoTime(), что приведет к отрицательной разнице. Это логично. Счетчики между процессорами не синхронизированы.
  • Во многих случаях вы можете получить довольно вводящие в заблуждение результаты, но вы не сможете сказать, потому что дельта не является отрицательной. Подумайте об этом.
  • (неподтвержденный) Я думаю, что вы можете получить отрицательный результат даже на том же процессоре, если инструкции переупорядочены. Чтобы этого не произошло, вам придется вызывать барьер памяти, который выполняет сериализацию ваших инструкций.

Было бы здорово, если System.nanoTime() вернул coreID, где он был выполнен.

Ответ 11

Java является кроссплатформенной, а nanoTime зависит от платформы. Если вы используете Java - когда не используете nanoTime. Я нашел реальные ошибки в разных реализациях jvm с помощью этой функции.

Ответ 12

В документации по Java 5 также рекомендуется использовать этот метод для этой же цели.

Этот метод можно использовать только для измерять прошедшее время и не связанных с любым другим понятием системы или настенные часы.

Java 5 API Doc

Ответ 13

Кроме того, при изменении системных часов изменяется System.currentTimeMillies(), а System.nanoTime() - нет, поэтому последний более безопасен для измерения продолжительности.

Ответ 14

Начиная с Java 7, System.nanoTime() гарантированно безопасен согласно спецификации JDK. System.nanoTime() Javadoc проясняет, что все наблюдаемые вызовы в JVM (то есть во всех потоках) являются монотонными:

Возвращаемое значение представляет наносекунды с некоторого фиксированного, но произвольного времени начала (возможно, в будущем, поэтому значения могут быть отрицательными). Один и тот же источник используется всеми вызовами этого метода в экземпляре виртуальной машины Java; другие экземпляры виртуальной машины могут использовать другое происхождение.

Реализация JVM/JDK отвечает за устранение несоответствий, которые могут наблюдаться при вызове базовых утилит ОС (например, упомянутых в ответе Тома Андерсона).

Большинство других старых ответов на этот вопрос (написано в 2009–2012 гг.) Выражают FUD, который, вероятно, имел отношение к Java 5 или Java 6, но больше не относится к современным версиям Java.

Однако стоит отметить, что, несмотря на то, что JDK гарантирует безопасность nanoTime(), в OpenJDK было несколько ошибок, из-за которых он не поддерживал эту гарантию на определенных платформах или при определенных обстоятельствах (например, JDK-8040140, JDK-8184271). На данный момент в OpenJDK нет открытых (известных) ошибок по nanoTime(), но обнаружение новой такой ошибки или регрессии в новой версии OpenJDK, вероятно, не должно шокировать.

Имея это в виду, код, который использует nanoTime() для nanoTime() блокировки, ожидания интервала, тайм-аутов и т.д., Предпочтительно должен рассматривать отрицательные различия во времени (тайм-ауты) как нули, а не как исключения. Эта практика также предпочтительнее, поскольку она согласуется с поведением всех методов Lock.tryLock() ожидания во всех классах в java.util.concurrent.*, Например, Semaphore.tryAcquire(), Lock.tryLock(), BlockingQueue.poll() и т.д.,

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

Вместо того, чтобы использовать nanoTime() для непосредственного измерения времени выполнения кода, предпочтительно использовать специализированные тестовые среды и профилировщики, например JMH и асинхронный профилировщик в режиме профилирования настенных часов.

Ответ 15

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