System.currentTimeMillis против System.nanoTime - PullRequest
351 голосов
/ 09 декабря 2008

Точность против. Точность

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

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

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

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

Ответы [ 10 ]

304 голосов
/ 09 декабря 2008

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

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

public static long nanoTime()

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

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

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

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

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

89 голосов
/ 16 февраля 2012

Так как никто другой не упомянул об этом ...

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

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

58 голосов
/ 09 декабря 2008

Обновление Аркадий : я наблюдал более правильное поведение 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() приведет к изменению разрешения.

10 голосов
/ 09 декабря 2008

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

Что касается точности, вы почти правы. На некоторых компьютерах с Windows разрешение currentTimeMillis() составляет около 10 мс (не 50 мс). Я не уверен почему, но некоторые машины с Windows так же точны, как машины с Linux.

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

8 голосов
/ 25 сентября 2013

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

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

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

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

5 голосов
/ 09 декабря 2008

Да, если требуется такая точность, используйте 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)); }
    }
2 голосов
/ 17 августа 2017

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

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

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

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

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

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

0 голосов
/ 28 ноября 2017

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

О методе nanoTime:

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

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

0 голосов
/ 24 апреля 2012

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

...