System.currentTimeMillis против System.nanoTime

Точность Vs. Точность

Я хотел бы знать, следует ли мне использовать System.currentTimeMillis () или System.nanoTime () при обновлении позиций моего объекта в моей игре? Их изменение в движении прямо пропорционально времени, прошедшему с момента последнего звонка, и я хочу быть максимально точным.

Я читал, что между разными операционными системами существуют серьезные проблемы с разрешением по времени (а именно, что Mac / Linux имеют разрешение почти 1 мс, а Windows - 50 мс ??). Я в основном запускаю свои приложения в Windows, и разрешение 50 мс кажется довольно неточным.

Есть ли варианты получше двух, которые я перечислил?

Есть предложения / комментарии?

nanoTime обычно значительно более точен, чем currentTimeMillis, но это также относительно дорогой вызов. currentTimeMillis() работает за несколько (5-6) тактов ЦП, nanoTime зависит от базовой архитектуры и может составлять более 100 тактов ЦП.
bestsss 21.02.2011 04:15

Вы все понимаете, что Windows обычно имеет дискретность временного кванта 1000 мс / 64, не так ли? Это 15,625 мс, или 15625000 наносекунд!

user968961 28.09.2011 15:30

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

aglassman 13.06.2012 19:58

Windows имеет размерность временного кванта ПО УМОЛЧАНИЮ 1000 мс / 64. Вы можете увеличить это значение с помощью собственного API timeBeginPeriod. Современные ПК также имеют таймеры с высоким разрешением в дополнение к базовому таймеру. Таймеры с высоким разрешением доступны через вызов QueryPerformanceCounter.

Robin Davies 16.01.2013 12:11

@bestsss как узнать использование тактовых частот процессора?

Gohan 23.07.2015 05:19

@Gohan - В этой статье подробно рассказывается о внутренней работе System.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.htm‌ l

Attila Tanyi 13.08.2017 13:33
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
390
6
399 795
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Ответ принят как подходящий

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

Из документации Java:

public static long nanoTime()

Returns the current value of the most precise available system timer, in nanoseconds.

This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary origin time (perhaps in the future, so values may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees are made about how frequently values change. Differences in successive calls that span greater than approximately 292 years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.

Например, чтобы измерить, сколько времени требуется для выполнения некоторого кода:

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;

См. Также: JavaDoc System.nanoTime () и JavaDoc System.currentTimeMillis () для получения дополнительной информации.

Вы уверены, что знаете разницу между точностью и точностью? Нет никакого способа, чтобы это было с точностью до наносекунды.

mmcdole 09.12.2008 05:09

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

dancavallaro 09.12.2008 05:10

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

dancavallaro 09.12.2008 05:11

@dancavallaro, спасибо за информацию. Если вы не возражаете, я отредактировал ваш ответ, включив цитату из документов, и исправил ссылки.

mmcdole 09.12.2008 05:28

последовательные звонки продолжительностью более 292 лет .... моя новая любимая цитата!

akuhn 09.12.2008 07:51

Этот ответ технически правильный при выборе nanoTime (), но полностью игнорирует чрезвычайно важный момент. nanoTime (), как говорится в документе, - это точный таймер. currentTimeMillis () НЕ ТАЙМЕР, это «настенные часы». nanoTime () всегда будет давать положительное истекшее время, а currentTimeMillis - нет (например, если вы измените дату, достигнете дополнительной секунды и т. д.). Это чрезвычайно важное различие для некоторых типов систем.

charstar 10.04.2011 14:32

Пользователь меняет время и синхронизацию NTP, но почему currentTimeMillis изменится из-за перехода на летнее время? Переключатель DST не меняет количество секунд после эпохи. Это могут быть «настенные часы», но они основаны на UTC. Вы должны определить в зависимости от вашего часового пояса и настроек летнего времени, во что это переводится для вашего местного времени (или используйте другие утилиты Java, чтобы сделать это за вас).

Shadow Man 25.01.2014 01:43

System.nanoTime () не всегда монотонный steveloughran.blogspot.com/2015/09/…

Tarun Chabarwal 02.11.2019 10:25

System.nanoTime() не поддерживается в старых JVM. Если это вызывает беспокойство, придерживайтесь currentTimeMillis.

Что касается точности, то вы почти правы. На НЕКОТОРЫХ машинах с Windows currentTimeMillis() имеет разрешение около 10 мс (а не 50 мс). Я не уверен, почему, но некоторые машины с Windows так же точны, как и машины с Linux.

Раньше я использовал GAGETimer с умеренным успехом.

"старые JVM" как в каких? Java 1.2 или что-то в этом роде?

Simon Forsberg 20.10.2017 18:07

System.nanoTime () был представлен в Java 1.5 в 2004 году. Java 1.4 расширила поддержку в 2013 году, поэтому можно с уверенностью сказать, что на System.nanoTime () теперь можно положиться, и этот ответ уже устарел.

Dave L. 22.01.2020 19:45

Да, если требуется такая точность, используйте System.nanoTime(), но имейте в виду, что тогда вам потребуется Java 5+ JVM.

В моих системах XP я вижу, что системное время сообщается как минимум на 100 микросекунд 278 наносекунды, используя следующий код:

private void test() {
    System.out.println("currentTimeMillis: "+System.currentTimeMillis());
    System.out.println("nanoTime         : "+System.nanoTime());
    System.out.println();

    testNano(false);                                                            // to sync with currentTimeMillis() timer tick
    for(int xa=0; xa<10; xa++) {
        testNano(true);
        }
    }

private void testNano(boolean shw) {
    long strMS=System.currentTimeMillis();
    long strNS=System.nanoTime();
    long curMS;
    while((curMS=System.currentTimeMillis()) == strMS) {
        if (shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)); }
        }
    if (shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)+", Milli: "+(curMS-strMS)); }
    }

Обновление от Аркадий: я наблюдал более правильное поведение System.currentTimeMillis() в Windows 7 в Oracle Java 8. Время возвращалось с точностью до 1 миллисекунды. Исходный код в OpenJDK не изменился, поэтому я не знаю, что вызывает лучшее поведение.


Дэвид Холмс из Sun опубликовал статью в блоге пару лет назад, в которой очень подробно рассматриваются API-интерфейсы синхронизации Java (в частности, System.currentTimeMillis() и System.nanoTime()), когда вы хотите их использовать и как они работают внутри компании.

Внутри виртуальной машины Hotspot: часы, таймеры и планирование событий - Часть I - Windows

Один очень интересный аспект таймера, используемого Java в Windows для API-интерфейсов, которые имеют параметр синхронизированного ожидания, заключается в том, что разрешение таймера может меняться в зависимости от того, какие другие вызовы API могли быть сделаны - в масштабе всей системы (а не только в конкретном процессе). . Он показывает пример, в котором использование Thread.sleep() вызовет это изменение разрешения.

@Arkadiy: У вас есть источник заявления в обновлении?

Lii 15.12.2015 20:40

@Lii - К сожалению, нет. Это исходит от того, что я запустил код в этом вопросе: stackoverflow.com/questions/34090914/…. Код обеспечивает точность 15 мс с Java 7 и точность 1 мс с Java 8.

user3458 15.12.2015 21:01

Я проследил currentTimeMillis до os :: javaTimeMillis в hotspot / src / os / windows / vm / os_windows.cpp в OpenJDK (hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src‌ / os /…). Похоже, это все еще GetSystemTimeAsFileTime, поэтому я не знаю, откуда взялись изменения. Или даже если это действительно так. Перед использованием проверьте.

user3458 16.12.2015 20:44

изменение в поведении является результатом изменения того, как GetSystemTimeAsFileTime работал в XP против 7. См. здесь для более подробной информации (tl; dr это стало более точным, поскольку вся система ввела некоторые более точные методы синхронизации).

user719662 19.07.2017 01:08

У меня был хороший опыт работы с нанотремя. Он обеспечивает время настенных часов в виде двух длин (секунды с начала эпохи и наносекунды в пределах этой секунды) с использованием библиотеки JNI. Он доступен с предварительно скомпилированной частью JNI как для Windows, так и для Linux.

Поскольку никто об этом не упомянул ...

Сравнивать результаты вызовов System.nanoTime() между разными потоками небезопасно. Даже если события потоков происходят в предсказуемом порядке, разница в наносекундах может быть положительной или отрицательной.

System.currentTimeMillis() безопасен для использования между потоками.

В Windows это держится только до SP2 в соответствии с: stackoverflow.com/questions/510462/…

Peter Schmitz 22.02.2012 15:25

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

gub 04.03.2012 18:55

@jgubby: Очень интересно ... есть ли ссылка на поддержку этого небезопасно сравнивать результаты вызовов System.nanoTime () между разными потоками? Стоит посмотреть следующие ссылки: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418docs.oracle.com/javase/7/docs/api/java/lang/…

user454322 09.10.2012 14:45

Глядя на описание, упомянутое здесь: docs.oracle.com/javase/7/docs/api/java/lang/…, кажется, что разница между значениями, возвращаемыми из nanoTime, действительна для сравнения, если они были от одной и той же JVM - The values returned by this method become meaningful only when the difference between two such values, obtained within the same instance of a Java virtual machine, is computed.

Tuxdude 21.01.2016 02:01

Это не совсем правильно, nanoTime () возвращает последовательно по потокам, если ваша система не содержит ошибок, это число будет увеличиваться между вызовами, как ожидалось. С другой стороны, System.currentTimeMillis () может перемещаться назад даже в том же потоке, потому что представляет собой время стены, которое можно регулировать внешними факторами, такими как обновления NTP.

David 16.08.2017 23:26

Я не вижу смысла вообще измерять время, прошедшее между разными потоками. Но в любом случае я хотел бы добавить «цитату» к вашему заявлению о том, что System.nanoTime() не является потокобезопасным.

Simon Forsberg 20.10.2017 18:15
JavaDoc для nanoTime() говорит: Один и тот же источник используется для всех вызовов этого метода в экземпляре виртуальной машины Java; другие экземпляры виртуальных машин могут использовать другое происхождение., что означает, что делает возвращает то же самое по потокам.
Simon Forsberg 01.02.2018 19:02

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

leventov 04.02.2019 06:01

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

Как отмечено в других ответах и ​​комментариях, currentTimeMillis подвержен изменениям системных часов и, следовательно, плохой выбор для расчета времени, прошедшего с момента некоторого более раннего события в JVM.

duelin markers 14.10.2013 23:00

Как уже говорили другие, currentTimeMillis - это время часов, которое изменяется из-за перехода на летнее время (нет: переход на летнее время и часовой пояс не связаны с currentTimeMillis, остальное верно), пользователей, меняющих настройки времени, дополнительных секунд и синхронизации времени в Интернете. Если ваше приложение зависит от монотонно увеличивающихся значений прошедшего времени, вы можете предпочесть nanoTime.

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

Если вы хотите использовать время часов, но избегаете прерываний из-за синхронизации времени в Интернете, вы можете рассмотреть клиент NTP, такой как Meinberg, который «настраивает» тактовую частоту на ноль, вместо того, чтобы просто периодически сбрасывать часы.

Я говорю из личного опыта. В приложении для погоды, которое я разработал, я получал случайные всплески скорости ветра. Мне потребовалось время, чтобы понять, что моя временная шкала нарушается из-за поведения часов на типичном ПК. Все мои проблемы исчезли, когда я начал использовать nanoTime. Последовательность (монотонность) была важнее для моего приложения, чем грубая точность или абсолютная точность.

"currentTimeMillis - это время часов, которое меняется в связи с переходом на летнее время" ... почти уверен, что это утверждение неверно. System.currentTimeMillis() сообщает время, прошедшее с начала эпохи Unix / Posix (в миллисекундах), то есть полуночи 1 января 1970 года по всемирному координированному времени. Поскольку UTC никогда не корректируется для перехода на летнее время, это значение не будет смещаться, когда в местных часовых поясах добавляются или применяются смещения местного времени для перехода на летнее время. Более того, Java Time Scale сглаживает дополнительные секунды, так что дни по-прежнему имеют 86400 секунд.
scottb 23.05.2016 08:27

Для игровой графики и плавного обновления положения используйте System.nanoTime(), а не System.currentTimeMillis(). Я переключился с currentTimeMillis () на nanoTime () в игре и получил значительное визуальное улучшение плавности движения.

Хотя может показаться, что одна миллисекунда уже должна быть точной, визуально это не так. Факторы, которые может улучшить nanoTime(), включают:

  • точное позиционирование пикселей ниже разрешения настенных часов
  • возможность сглаживания между пикселями, если хотите
  • Неточность настенных часов на окнах
  • дрожание часов (несоответствие фактического времени, когда настенные часы тикают вперед)

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

System.currentTimeMillis() небезопасен для истекшего времени, потому что этот метод чувствителен к системным изменениям часов реального времени системы. Вам следует использовать System.nanoTime. См. Справку по системе Java:

О методе nanoTime:

.. This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis()..

Если вы используете System.currentTimeMillis(), ваше прошедшее время может быть отрицательным (Назад <- в будущее)

Другие вопросы по теме