Ответ 1
Я предполагаю, что поскольку System.nanoTime()
использует "самый точный доступный системный таймер", который, по-видимому, имеет только миллисекундную точность в вашей системе, вы не можете получить ничего лучше.
Я видел в Интернете, что я должен использовать 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(). Уф.
Я предполагаю, что поскольку System.nanoTime()
использует "самый точный доступный системный таймер", который, по-видимому, имеет только миллисекундную точность в вашей системе, вы не можете получить ничего лучше.
Мне не ясно, что именно вы сравниваете, но, как правило, любой тест, который занимает такой короткий промежуток времени, что точность менее 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, чтобы узнать, что вам нужно знать?
Вы должны повторять тесты тысячи раз. Есть много вещей, которые будут влиять на ваши измерения, такие как сбор мусора, ввод-вывод, ввод/выключение подкачки, размер готовых потоков очереди и т.д.
FYI, Java 9 и более поздние версии имеют новую реализацию Clock
, которая может отображать текущий момент до разрешения наносекунд.
Instant
класс представляет момент на временной шкале в UTC с разрешением наносекунды (до девяти (9) цифр десятичной дроби).
Вызовите Instant.now
, чтобы зафиксировать текущий момент.
В Java 8 текущий момент фиксируется только до миллисекунды (вы действительно можете хранить значения с наносекундами, но только фиксировать текущий момент в миллисекундах).
Мгновенный момент = Instant.now();
Представьте промежуток времени, не привязанный к временной шкале, с классом Duration
. Удерживает количество времени в секундах и наносекундах.
Duration d = Duration.between( instantThen , Instant.now() );
Чтобы быть ясным, разрешение microseconds, заданное в Вопросе, находится между деталями миллисекунд и наносекундами. Число мест в десятичной дроби: миллис составляет 3 (0,123), микроны - 6 (0,123456), нано - 9 (0,123456789).
Java использует ваши аппаратные часы. Как сообщали другие, это оборудование почти наверняка захватит время с гораздо меньшей точностью и гораздо меньше разрешения, чем наносекунды.
Бенчмаркинг при такой тонкой детализации чреват проблемами и не рекомендуется в целом.
И остерегайтесь преждевременной оптимизации.
Предлагается добавить средство для микро-бенчмаркинга на платформу Java в JEP 230: Microbenchmark Suite. На основе Java Microbenchmark Harness (JMH).
Структура java.time встроена в Java 8 и более поздних версий. Эти классы вытесняют неприятный старый legacy классы времени, такие как java.util.Date
, Calendar
и SimpleDateFormat
.
Проект Joda-Time, теперь режим обслуживания, советуем перейти к классам java.time.
Чтобы узнать больше, см. Учебник Oracle. И поиск Qaru для многих примеров и объяснений. Спецификация JSR 310.
Где получить классы java.time?
Проект ThreeTen-Extra расширяет java.time с помощью дополнительных классов. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные классы, такие как Interval
, YearWeek
, YearQuarter
и больше.
Если вы хотите получить достоверный результат, используйте профилировщик. Я предлагаю VisualVM, который прост в установке и поставляется вместе с JDK, начиная с версии 1.6.0_07. Это простой в использовании визуальный инструмент, который объединяет несколько инструментов JDK с командной строкой и облегченные возможности профилирования.
Возможно, базовая ОС не обеспечивает таймеры с точностью до наносекунды.
Существует также более старое сообщение.
Да, точность и точность System.nanoTime обычно намного лучше, чем System.currentTimeMillis, но без гарантии: в худшем случае он может стать столь же плохим.
ThreadMXBean.getCurrentThreadCpuTime имеет тенденцию давать меньше времени, но его разрешение неясно, и у него есть дополнительные недостатки (вы действительно хотите время процессора?, зависимую от платформы семантику, поддерживаемую на вашей платформе?).
Измерение времени со всеми тремя методами также имеет некоторую стоимость, т.е. требует самого времени, которое может искажать измерения. Затраты сильно зависят от платформы, но часто стоят (System.currentTimeMillis) < (System.nanoTime) < Стоимость (ThreadMXBean.getCurrentThreadCpuTime).
О микро-бенчмаркинге в целом см.
Такой критерий, который опирается на короткий интервал времени, дает вам ненадежные результаты. Вы всегда будете получать разные результаты из-за внешних факторов, таких как I/O, Swapping, Process Switches, Caches, Garbage Collection и т.д. Кроме того, JVM оптимизирует ваши вызовы, поэтому вероятность того, что первые измеренные вещи будут медленнее, чем позже, будет вызвана. JVM запускает все больше и больше, чтобы оптимизировать команды, которые вы выполняете.
Кроме того, такой метод, как System.nanoTime(), зависит от таймеров базовой системы. Они могут (и, скорее всего, будут) не иметь гранулярности для измерения с такой точностью. Чтобы привести API:
Этот метод обеспечивает наносекунду точность, но не обязательно точность наносекунд. Нет гарантий о том, как часто значения изменение.
Чтобы действительно измерить с высокой точностью, вам необходимо получить доступ к внешнему таймеру с гарантированной точностью.
Чтобы сделать ваш тест более стабильным, вам нужно выполнить его более одного раза и измерить большие интервалы времени, чем только миллисекунды.
"быстрое и грязное" решение, с которым я в конечном итоге пошел:
TimeUnit.NANOSECONDS.toMicros(System.nanoTime());
UPDATE:
Я изначально пошел с 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