Как измерить время с точностью до микросекунды в Java?

Я видел в Интернете, что я должен использовать System.nanoTime (), но это не работает для меня – это дает мне время с точностью до миллисекунд. Мне просто нужны микросекунды до и после выполнения моей функции, чтобы я знал, сколько времени потребуется. Я использую Windows XP.

В принципе, у меня есть этот код, который, например, составляет 1 миллион до 10 миллионов вставок в java-связанном списке. Проблема в том, что я не могу правильно измерить точность; иногда требуется меньше времени, чтобы вставить все в меньший список.

Вот пример:

class test { public static void main(String args[]) { for(int k=1000000; k<=10000000; k+=1000000) { System.out.println(k); LinkedList aux = new LinkedList(); //need something here to see the start time for(int i=0; i<k; i++) aux.addFirst(10000); //need something here to see the end time //print here the difference between both times } } } 

Я делал это много раз – внешняя петля делала это 20 раз для каждого k, но результат не очень хорош. Иногда требуется меньше времени, чтобы сделать 10 миллионов вставок, чем 1 миллион, потому что я не получаю правильное измеренное время с тем, что я сейчас использую (System.nanoTime ())

Edit 2: Да, я использую Sun JVM.

Редактирование 3: Возможно, я сделал что-то не так в коде, я посмотрю, изменит ли он то, что я хочу.

Изменить 4: Моя ошибка, похоже, работает System.nanoTime (). Уф.

Я предполагаю, что поскольку System.nanoTime() использует «самый точный ansible системный таймер», который, по-видимому, имеет только миллисекундную точность в вашей системе, вы не можете получить ничего лучше.

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

Обычно я пытаюсь запустить тесты не менее 10 секунд. Структура, которую я пишу в данный момент, догадается, сколько итераций запускается так, что это займет 30 секунд. Это означает, что вы не получите радикально разных результатов только потому, что какой-то другой процесс украл процессор на несколько миллисекунд.

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

System.nanoTime () использует счетчик в процессоре и обычно с точностью до 1 микросекунды в Windows XP и Linux.

Примечание. Windows XP часто бывает менее точной на машинах с несколькими процессорами, поскольку она не компенсирует разные процессоры, имеющие разные счетчики. Linux делает. Примечание 2: он будет дрейфовать относительно System.currentTimeMillis (), поскольку он основан на точности часов для вашего процессора (который не обязательно должен быть таким точным в течение определенного периода времени), а не на часах, которые у вас есть для получения времени. (которое дрейфует меньше в день, но имеет меньшую степень детализации)

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

Попробуйте запустить тесты со следующими параметрами, и вы увидите очень разные результаты.

-verbosegc -XX: NewSize = 128m -mx256m

Это странно. Предполагается, что работает System.nanoTime (). Вы используете Sun JVM?

Можете ли вы просто повторить свою операцию 1000 раз и разделить время на 1000, чтобы узнать, что вам нужно знать?

Вы должны повторять тесты тысячи раз. Есть много вещей, которые будут влиять на ваши измерения, такие как garbage collection, ввод-вывод, ввод / выключение подкачки, размер готовых streamов очереди и т. Д.

Использование java.time

FYI, Java 9 и более поздние версии имеют новую реализацию Clock которая может фиксировать текущий момент до разрешения наносекунд.

Instant class представляет момент на временной шкале в UTC с разрешением наносекунд (до девяти (9) цифр десятичной дроби).

Вызовите Instant.now чтобы зафиксировать текущий момент.

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

    Instant Instant = Instant.now ();

Представьте промежуток времени, не привязанный к временной шкале с classом Duration . Удерживает количество времени в секундах и наносекундах.

 Duration d = Duration.between( instantThen , Instant.now() ); 

Чтобы быть ясным, разрешение микросекунд , заданное в Вопросе, находится между деталями миллисекунд и наносекундами. Число мест в десятичной дроби: миллис составляет 3 (0,123), микроны – 6 (0,123456), нано – 9 (0,123456789).

Предостережение

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

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

И остерегайтесь преждевременной оптимизации .

Предлагается добавить средство для микро-бенчмаркинга на платформу Java в JEP 230: Microbenchmark Suite . На основе Java Microbenchmark Harness (JMH) .


О java.time

Рамка java.time встроена в Java 8 и более поздние версии . Эти classы вытесняют неприятные старые устаревшие classы времени, такие как java.util.Date , Calendar и SimpleDateFormat .

Проект Joda-Time , теперь в режиме обслуживания , советует перейти на classы java.time .

Чтобы узнать больше, ознакомьтесь с учебным пособием Oracle . И поиск Stack Overflow для многих примеров и объяснений. Спецификация – JSR 310 .

Где можно получить classы java.time?

  • Java SE 8 и SE 9 и более поздние версии
    • Встроенный.
    • Часть стандартного Java API с интегрированной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функциональных возможностей java.time включена обратно в Java 6 и 7 в ThreeTen-Backport .
  • Android
    • Проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше) специально для Android.
    • См. Раздел Как использовать ThreeTenABP ….

Проект ThreeTen-Extra расширяет java.time с дополнительными classами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные classы, такие как Interval , YearWeek , YearQuarter и другие .

Если вы хотите получить достоверный результат, используйте профилировщик. Я предлагаю VisualVM , который прост в установке и поставляется вместе с JDK, начиная с версии 1.6.0_07. Это простой в использовании визуальный инструмент, который объединяет несколько инструментов JDK с командной строкой и облегченные возможности профилирования.

Возможно, базовая ОС не обеспечивает таймеры с точностью до наносекунд.

Существует также более старая должность .

Да, точность и точность System.nanoTime обычно намного лучше, чем System.currentTimeMillis, но без гарантии: в худшем случае он может стать столь же плохим.

ThreadMXBean.getCurrentThreadCpuTime имеет тенденцию давать меньше времени, но его разрешение неясно, и у него есть и другие недостатки (вы действительно хотите время процессора?, Зависимую от платформы семантику, поддерживаемую на вашей платформе?).

Измерение времени со всеми тремя методами также имеет некоторую стоимость, т. Е. Требует самого времени, которое может исказить измерения. Затраты зависят от платформы, но часто стоят (System.currentTimeMillis) << cost (System.nanoTime) << cost (ThreadMXBean.getCurrentThreadCpuTime).

О микро-бенчмаркинге в целом см.

  • Как написать правильный микро-тест в Java?
  • Создать быстрый / надежный тест с java?

Такой критерий, который опирается на короткий интервал времени, дает вам ненадежные результаты. Вы всегда будете получать разные результаты из-за внешних факторов, таких как I / O, Swapping, Process Switches, Caches, Garbage Collection и т. Д. Кроме того, JVM оптимизирует ваши вызовы, поэтому вполне вероятно, что первые измеренные вещи идут медленнее, чем позже. JVM запускает все больше и больше, чтобы оптимизировать команды, которые вы выполняете.

Кроме того, такой метод, как System.nanoTime (), зависит от таймеров базовой системы. Они могут (и, скорее всего, будут) не иметь гранулярности для измерения с такой точностью. Чтобы привести API :

Этот метод обеспечивает наносекундную точность, но не обязательно наносекундную точность. Никаких гарантий относительно того, как часто меняются ценности, не делается.

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

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

«быстрое и грязное» решение, с которым я в конечном итоге пошел:

 TimeUnit.NANOSECONDS.toMicros(System.nanoTime()); 

ОБНОВИТЬ:

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

 TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); 

но это просто добавит нули в конце значения (micros = millis * 1000)

Оставил этот ответ здесь как «предупреждающий знак», если кто-то еще подумает о nanoTime 🙂

Для нашего недавнего профилирования я обнаружил, что ThreadMXBean.getCurrentThreadCpuTime () и опция -XX: + UseLinuxPosixThreadCPUClocks сделали то, что нам было нужно.

См. http://bugs.java.com/view_bug.do?bug_id=6888526 для получения дополнительной информации.

Давайте будем гением компьютера.