Как я могу измерить время с точностью до микросекунды в 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<Integer> aux = new LinkedList<Integer>();
            //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())

Изменить 2: Да, я использую Sun JVM.

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

Редактировать 4: Моя ошибка, кажется, System.nanoTime() работает. Уф.

12 ответов

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

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

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

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

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

К вашему сведению, Java 9 и новее имеет свежую реализацию Clock это может захватить текущий момент с точностью до наносекунд.

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

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

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

    Мгновенное мгновение = Instant.now();

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

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

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

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

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

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

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

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


О java.time

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

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

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

Где взять классы java.time?

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

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

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

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

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

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

-verbosegc -XX: NewSize = 128m -mx256m

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

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

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

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

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

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

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

Про микро бенчмаркинг в общем смотрите

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

Есть также более старый пост.

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

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

ОБНОВИТЬ:

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

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

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

Оставьте этот ответ здесь как "предупреждающий знак" на случай, если кто-то еще подумает о nanoTime:)

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

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

Такой тест, который опирается на короткий промежуток времени, дает вам ненадежные результаты. Вы всегда будете получать разные результаты из-за внешних факторов, таких как ввод-вывод, перестановка, переключение процессов, кеширование, сборка мусора и т. Д. Кроме того, JVM оптимизирует ваши вызовы, поэтому вероятно, что первые измеренные события будут выполняться медленнее, чем последующие вызовы. JVM начинает все больше и больше оптимизировать команды, которые вы выполняете.

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

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

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

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

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