Я хотел бы знать, следует ли мне использовать System.currentTimeMillis () или System.nanoTime () при обновлении позиций моего объекта в моей игре? Их изменение в движении прямо пропорционально времени, прошедшему с момента последнего звонка, и я хочу быть максимально точным.
Я читал, что между разными операционными системами существуют серьезные проблемы с разрешением по времени (а именно, что Mac / Linux имеют разрешение почти 1 мс, а Windows - 50 мс ??). Я в основном запускаю свои приложения в Windows, и разрешение 50 мс кажется довольно неточным.
Есть ли варианты получше двух, которые я перечислил?
Есть предложения / комментарии?
Вы все понимаете, что Windows обычно имеет дискретность временного кванта 1000 мс / 64, не так ли? Это 15,625 мс, или 15625000 наносекунд!
Я не думаю, что сотня дополнительных тактовых циклов повлияет на вашу игру, и компромисс, вероятно, того стоит. Вы должны вызывать метод только один раз за обновление игры, а затем сохранять значение в памяти, чтобы не было дополнительных накладных расходов. Что касается детализации различных платформ, я понятия не имею.
Windows имеет размерность временного кванта ПО УМОЛЧАНИЮ 1000 мс / 64. Вы можете увеличить это значение с помощью собственного API timeBeginPeriod. Современные ПК также имеют таймеры с высоким разрешением в дополнение к базовому таймеру. Таймеры с высоким разрешением доступны через вызов QueryPerformanceCounter.
@bestsss как узнать использование тактовых частот процессора?
@Gohan - В этой статье подробно рассказывается о внутренней работе System.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.htm l




Если вы просто ищете чрезвычайно точные измерения пройденное время, используйте 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 () для получения дополнительной информации.
Вы уверены, что знаете разницу между точностью и точностью? Нет никакого способа, чтобы это было с точностью до наносекунды.
Извините, я имел в виду точный. Я использовал этот термин вольно, но согласен, что это сбивало с толку (и неправильное использование слова).
А поскольку вам нужно затраченное время, вам не важна точность, вам важна точность. System.nanoTime () предоставит вам наиболее точный временной интервал.
@dancavallaro, спасибо за информацию. Если вы не возражаете, я отредактировал ваш ответ, включив цитату из документов, и исправил ссылки.
последовательные звонки продолжительностью более 292 лет .... моя новая любимая цитата!
Этот ответ технически правильный при выборе nanoTime (), но полностью игнорирует чрезвычайно важный момент. nanoTime (), как говорится в документе, - это точный таймер. currentTimeMillis () НЕ ТАЙМЕР, это «настенные часы». nanoTime () всегда будет давать положительное истекшее время, а currentTimeMillis - нет (например, если вы измените дату, достигнете дополнительной секунды и т. д.). Это чрезвычайно важное различие для некоторых типов систем.
Пользователь меняет время и синхронизацию NTP, но почему currentTimeMillis изменится из-за перехода на летнее время? Переключатель DST не меняет количество секунд после эпохи. Это могут быть «настенные часы», но они основаны на UTC. Вы должны определить в зависимости от вашего часового пояса и настроек летнего времени, во что это переводится для вашего местного времени (или используйте другие утилиты Java, чтобы сделать это за вас).
System.nanoTime () не всегда монотонный steveloughran.blogspot.com/2015/09/…
System.nanoTime() не поддерживается в старых JVM. Если это вызывает беспокойство, придерживайтесь currentTimeMillis.
Что касается точности, то вы почти правы. На НЕКОТОРЫХ машинах с Windows currentTimeMillis() имеет разрешение около 10 мс (а не 50 мс). Я не уверен, почему, но некоторые машины с Windows так же точны, как и машины с Linux.
Раньше я использовал GAGETimer с умеренным успехом.
"старые JVM" как в каких? Java 1.2 или что-то в этом роде?
System.nanoTime () был представлен в Java 1.5 в 2004 году. Java 1.4 расширила поддержку в 2013 году, поэтому можно с уверенностью сказать, что на System.nanoTime () теперь можно положиться, и этот ответ уже устарел.
Да, если требуется такая точность, используйте 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 - К сожалению, нет. Это исходит от того, что я запустил код в этом вопросе: stackoverflow.com/questions/34090914/…. Код обеспечивает точность 15 мс с Java 7 и точность 1 мс с Java 8.
Я проследил currentTimeMillis до os :: javaTimeMillis в hotspot / src / os / windows / vm / os_windows.cpp в OpenJDK (hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src / os /…). Похоже, это все еще GetSystemTimeAsFileTime, поэтому я не знаю, откуда взялись изменения. Или даже если это действительно так. Перед использованием проверьте.
изменение в поведении является результатом изменения того, как GetSystemTimeAsFileTime работал в XP против 7. См. здесь для более подробной информации (tl; dr это стало более точным, поскольку вся система ввела некоторые более точные методы синхронизации).
У меня был хороший опыт работы с нанотремя. Он обеспечивает время настенных часов в виде двух длин (секунды с начала эпохи и наносекунды в пределах этой секунды) с использованием библиотеки JNI. Он доступен с предварительно скомпилированной частью JNI как для Windows, так и для Linux.
Поскольку никто об этом не упомянул ...
Сравнивать результаты вызовов System.nanoTime() между разными потоками небезопасно. Даже если события потоков происходят в предсказуемом порядке, разница в наносекундах может быть положительной или отрицательной.
System.currentTimeMillis() безопасен для использования между потоками.
В Windows это держится только до SP2 в соответствии с: stackoverflow.com/questions/510462/…
Что ж, каждый день узнаешь что-то новое. Однако я подозреваю, что, учитывая, что в прошлом это было небезопасно (определенно возвращает бессмысленные показания по потокам), такое использование, вероятно, все еще выходит за рамки спецификации, и поэтому, вероятно, следует избегать.
@jgubby: Очень интересно ... есть ли ссылка на поддержку этого небезопасно сравнивать результаты вызовов System.nanoTime () между разными потоками? Стоит посмотреть следующие ссылки: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418docs.oracle.com/javase/7/docs/api/java/lang/…
Глядя на описание, упомянутое здесь: 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.
Это не совсем правильно, nanoTime () возвращает последовательно по потокам, если ваша система не содержит ошибок, это число будет увеличиваться между вызовами, как ожидалось. С другой стороны, System.currentTimeMillis () может перемещаться назад даже в том же потоке, потому что представляет собой время стены, которое можно регулировать внешними факторами, такими как обновления NTP.
Я не вижу смысла вообще измерять время, прошедшее между разными потоками. Но в любом случае я хотел бы добавить «цитату» к вашему заявлению о том, что System.nanoTime() не является потокобезопасным.
nanoTime() говорит: Один и тот же источник используется для всех вызовов этого метода в экземпляре виртуальной машины Java; другие экземпляры виртуальных машин могут использовать другое происхождение., что означает, что делает возвращает то же самое по потокам.
Это неверный ответ. Дело обстоит как раз наоборот: currentTimeMillis () не является потокобезопасным (в предполагаемом значении, то есть монотонно неубывающими результатами) даже в пределах одного потока. nanoTime () является потокобезопасным.
одна вещь здесь - непоследовательность метода nanoTime. он не дает очень согласованных значений для одного и того же ввода. currentTimeMillis намного лучше с точки зрения производительности и согласованности, а также, хотя и не так точен, как nanoTime, имеет меньшую погрешность. , и, следовательно, большая точность в его стоимости. Поэтому я бы посоветовал вам использовать currentTimeMillis
Как отмечено в других ответах и комментариях, currentTimeMillis подвержен изменениям системных часов и, следовательно, плохой выбор для расчета времени, прошедшего с момента некоторого более раннего события в JVM.
Как уже говорили другие, currentTimeMillis - это время часов, которое изменяется из-за перехода на летнее время (нет: переход на летнее время и часовой пояс не связаны с currentTimeMillis, остальное верно), пользователей, меняющих настройки времени, дополнительных секунд и синхронизации времени в Интернете. Если ваше приложение зависит от монотонно увеличивающихся значений прошедшего времени, вы можете предпочесть nanoTime.
Вы можете подумать, что игроки не будут возиться с настройками времени во время игры, и, возможно, вы будете правы. Но не стоит недооценивать сбой из-за синхронизации времени в Интернете или, возможно, пользователей удаленных рабочих столов. API nanoTime невосприимчив к такого рода сбоям.
Если вы хотите использовать время часов, но избегаете прерываний из-за синхронизации времени в Интернете, вы можете рассмотреть клиент NTP, такой как Meinberg, который «настраивает» тактовую частоту на ноль, вместо того, чтобы просто периодически сбрасывать часы.
Я говорю из личного опыта. В приложении для погоды, которое я разработал, я получал случайные всплески скорости ветра. Мне потребовалось время, чтобы понять, что моя временная шкала нарушается из-за поведения часов на типичном ПК. Все мои проблемы исчезли, когда я начал использовать nanoTime. Последовательность (монотонность) была важнее для моего приложения, чем грубая точность или абсолютная точность.
System.currentTimeMillis() сообщает время, прошедшее с начала эпохи Unix / Posix (в миллисекундах), то есть полуночи 1 января 1970 года по всемирному координированному времени. Поскольку UTC никогда не корректируется для перехода на летнее время, это значение не будет смещаться, когда в местных часовых поясах добавляются или применяются смещения местного времени для перехода на летнее время. Более того, Java Time Scale сглаживает дополнительные секунды, так что дни по-прежнему имеют 86400 секунд.
Для игровой графики и плавного обновления положения используйте 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(), ваше прошедшее время может быть отрицательным (Назад <- в будущее)
nanoTimeобычно значительно более точен, чем currentTimeMillis, но это также относительно дорогой вызов.currentTimeMillis()работает за несколько (5-6) тактов ЦП, nanoTime зависит от базовой архитектуры и может составлять более 100 тактов ЦП.