Java - Как да измерваме времето с точност до микросекунди в Java
Видях в мрежата, че трябва да използвам System.nanoTime(), но това не работи за мен - дава ми времето с точност до милисекунди. Просто ми трябват микросекунди преди и след изпълнението на моята функция, за да знам колко време ще отнеме. Използвам Windows XP.
По принцип имам този код, който е например 1 милион до 10 милиона вмъквания в свързана Java връзка. Проблемът е, че не мога да измеря правилно точността; понякога отнема по-малко време, за да вмъкнете всичко в по-малък списък.
Правил съм това много пъти - външният цикъл е правил това 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, за да видите какво трябва да знаете?
Трябва да повтаряте тестовете хиляди пъти. Има много неща, които ще повлияят на вашите измервания, като събиране на отпадъци, I/O, пейджинг I/O, размер на нишките на готовата опашка и т.н.
Използване на java.time
За информация, Java 9 и по-нови версии имат нова реализация на часовника, която може да показва текущия момент до наносекунда резолюция.
Класът Instant представлява момент от времевата линия в UTC с наносекунда разделителна способност (до девет (9) десетични цифри).
Обадете се на Instant.now, за да заснемете текущия момент.
-
В Java 9 и по-нови засега получавате наносекунди резолюция.
В Java 8 текущият момент се улавя само до милисекунда (вие наистина можете да съхранявате стойности с наносекунди, но да улавяте текущия момент само в милисекунди).
Моментален момент = Instant.now();
Представлява период от време, който не е обвързан с времева линия с класа Duration. Съдържа времето в секунди и наносекунди.
За да бъде ясно, разделителната способност в микросекунди, дадена във въпроса, е между детайли от милисекунди и наносекунди. Броят знаци в десетичната запетая: милис е 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 и по-нови версии. Тези класове изместват неприятните стари класове за време като java.util.Date, Calendar и SimpleDateFormat.
За да научите повече, вижте ръководството за Oracle. И потърсете в Stack Overflow много примери и обяснения. JSR 310 спецификация.
Къде да получа класове по java.time?
- Java SE 8 иSE 9 и по-нови версии
- вградена.
- Част от стандартния Java API с обединено изпълнение.
- Java 9 добавя някои незначителни функции и поправки.
Проектът 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, но след това разбрах, че трябва да се използва само за изминало време, в крайна сметка промених кода си, за да работи с милисекунди или на някои места:
но това просто ще добави нули в края на стойността (микро = мили * 1000)
Оставете този отговор тук като „предупредителен знак“, ако някой друг се сети за nanoTime :)
За скорошното ни профилиране открих, че ThreadMXBean.getCurrentThreadCpuTime() и опцията -XX: + UseLinuxPosixThreadCPUClocks направиха това, от което се нуждаехме.
Такъв критерий, който разчита на кратък интервал от време, ви дава ненадеждни резултати. Винаги ще получавате различни резултати поради външни фактори като I/O, Swapping, Process Switches, Cache, Garbage Collection и др. Освен това JVM оптимизира вашите повиквания, така че шансовете първите измерени неща да са по-бавни, отколкото по-късно, ще бъдат изчислени. JVM работи все повече и повече, за да оптимизира командите, които изпълнявате.
Освен това метод като System.nanoTime() зависи от таймерите на основната система. Те може (и най-вероятно ще) нямат детайлността, за да измерват с такава точност. За да донесете API:
Този метод осигурява наносекунда точност, но не непременно наносекунда точност. Няма гаранции за това колко често се променят стойностите.
За да измервате наистина с висока точност, трябва да имате достъп до външен таймер с гарантирана точност.
За да направите теста си по-стабилен, трябва да го изпълнявате повече от веднъж и да измервате повече времеви интервали от простомилисекунди.