Как засечь время в java

Как замерить время выполнения

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

Замер времени с помощью currentTimeMills()

Это довольно простой способ измерить время. Метод System.currentTimeMillis() вернёт вам текущее время в миллисекундах. Его потребуется вызвать до выполнения нужной задачи и после, а затем вычислить разницу. В итоге мы узнаем время выполнения в миллисекундах:

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

Замер времени с помощью nanoTime()

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

Он используется аналогично:

Для получения значения в миллисекундах результат можно разделить на 1000:

Важно: Хотя метод nanoTime() может возвращать время в наносекундах, он не гарантирует, что значения между его вызовами будут обновляться с точностью до наносекунд.

Но всё же это более приемлемый вариант, чем System.currentTimeMillis().

Замер времени с помощью Instant и Duration

В Java 8 добавили новый java.time API. В частности, ля измерения времени подойдут два новых класса – Instant и Duration. Оба эти класса иммутабельны.

Instant обозначает момент времени с начала эпохи Unix (1970-01-01T00:00:00Z). Для создания момента мы используем метод Instant.now(). После того, как мы создали два момент, вычислим разницу в миллисекундах:

Рекомендуется использовать именно этот подход в Java 8 и выше.

Замер времени выполнения с помощью StopWatch

StopWatch – это класс из библиотеки Apache Commons Lang. Он работает как секундомер. Для его использования сначала требуется подключить библиотеку к проекту:

Теперь создадим экземпляр StopWatch. Затем начнём отсчёт с помощью метода start() и окончим отсчёт с помощью метода stop():

StopWatch удобно использовать тогда, когда в проекте уже подключена данная библиотека.

Исходный код

Заключение

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

Источник

Подсчёт времени выполнения метода через аннотацию

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

Наша аннотация будет помечать методы, для которых мы хотим посчитать время выполнения в миллисекундах или наносекундах и выводить результат через System.out.println.

Для начала создадим саму аннотацию:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

Поле interval служит для указания интервала времени (миллисекунды или наносекунды), поле format задаёт формат вывода результата.

Теперь, чтобы данная аннотация сработала как надо, нужно создать класс-обработчик расширяющий AbstractProcessor. В данном классе добавляется сохранение времени перед кодом метода, сам код метода копируется в блок try-finally, а блоке finally вычисляется затраченное методом время и выводится в консоль:

import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.TypeTags;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util. List ;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;

@SupportedAnnotationTypes( value = )
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class TimeAnnotationProcessor extends AbstractProcessor <

public static final String ANNOTATION_TYPE = «annotations.time.Time» ;
private JavacProcessingEnvironment javacProcessingEnv;
private TreeMaker maker;

@Override
public boolean process(Set annotations, RoundEnvironment roundEnv) <
if (annotations == null || annotations.isEmpty()) <
return false ;
>

final Elements elements = javacProcessingEnv.getElementUtils();

final TypeElement annotation = elements.getTypeElement(ANNOTATION_TYPE);

// Заменяем старый код метода на новый
((JCMethodDecl) blockNode).body.stats = newStatements;
>
>
>

protected JCBlock makePrintBlock(TreeMaker maker, JavacElements utils, Time time, JCVariableDecl var ) <
// Создаём вызов System.out.println
JCExpression printlnExpression = maker.Ident(utils.getName( «System» ));
printlnExpression = maker.Select(printlnExpression, utils.getName( «out» ));
printlnExpression = maker.Select(printlnExpression, utils.getName( «println» ));

// Форматируем результат
JCExpression formatExpression = maker.Ident(utils.getName( «String» ));
formatExpression = maker.Select(formatExpression, utils.getName( «format» ));

Для того чтобы компилятор java использовал наш управляющий класс, нужно создать файл META-INF/javax.annotation.processing.Processor, в котором должна быть прописана следующая строка:
annotations.time.TimeAnnotationProcessor

После этого собираем все наши файлы в annotations.jar и добавляем его в classpath к любому проекту.

Теперь, чтобы посчитать время выполнения метода, достаточно добавить к этому методу аннотацию Time и после его выполнения в консоль будет выведено затраченное методом время:

В результате в консоли увидим:
method time: 1000 ms

Источник

Java Specialist

Блог о памяти, сборщике мусора, многопоточности и производительности в java

Содержание

понедельник, 16 апреля 2012 г.

Измерение времени и засыпание потоков

System.currentTimeMillis()
System.nanoTime()

Второй метод использует специальные счетчики, не связанные с системными часами (хотя на некоторых платформах он и может быть реализован через них, но это скорее исключение нежели правило). Формально System.nanoTime() возвращает наносекунды, но последовательные вызовы этого метода вряд ли дадут точность больше микросекунд. На большинстве систем данный метод будет возвращать неубывающие значения, для чего ему может потребоваться внутренняя синхронизация, если он будет вызываться на разных процессорах. Поэтому производительность этого метода очень сильно зависит от железа, и на некоторых машинах запрос к этому методу может легко занимать больше времени, чем запрос к System.currentTimeMillis() [2].

Учитывая, относительность времени, возвращаемого данным методом, его невозможно использовать, скажем, для измерения времени передачи сообщения от одного бокса к другому. Хотя конечно можно измерить время полного round-trip, вычесть время проведенное на второй машине и поделить на два. Однако если у вас распределенное приложение и вам очень важно мереть время затраченное на определенных операциях, которые распределены по разным боксам, то вы можете написать нативный метод, который будет возвращать абсолютное время с большей точность. Я видел такой подход в одном из проектов, с которыми мне приходилось интегрироваться.

Thread.sleep() / Object#wait()

С помощью данных методов можно попросить текущий поток уснуть на определенное количество миллисекунд. Точность просыпания будет зависеть от размера интервала прерываний на вашей ОС. На Windows это обычно 10 мс (но на некотором железе может быть и 15 мс [4]). Однако длина этого интервала может быть изменена даже стандартными средствами java. Данное переключение происходит автоматически, если вы просите заснуть любой поток на время не кратное, текущему интервалу прерываний. Причем, когда данный поток проснется, ОС вернется обратно к штатному режиму.

Источник

Под капотом у Stopwatch

Введение

Использование DateTime

Использовать структуру DateTime для замера времени выполнения кода достаточно просто:

Свойство DateTime.Now — возвращает локальную текущую дату и время. Вместо свойства DateTime.Now можно использовать свойство DateTime.UtcNow — возвращающее текущую дату и время, но вместо локального часового пояса оно представляет их как время Utc, то есть как всемирное координированное время.

Несколько слов о структуре DateTime

Возможно, немногие задумывались о том, что из себя представляет структура DateTime. Значение структуры DateTime измеряется в 100-наносекундных единицах, называемых тактами, и точная дата представляется числом тактов прошедших с 00:00 1 января 0001 года нашей эры.

Например, число 628539264000000000 представляет собой 6 октября 1992 года 00:00:00.

Структура DateTime содержит единственное поле, которое и содержит количество прошедших тактов:

Что плохого в использовании DateTime?

Использовать свойство DateTime.Now для измерения временных интервалов не очень хорошая идея, и вот почему:

Вычисление свойства DateTime.Now основывается на DateTime.UtcNow, то есть сначала вычисляется координированное время, а потом к нему применяется смещение часового пояса.

Именно поэтому использовать свойство DateTime.UtcNow будет правильнее, оно вычисляется намного быстрее:

Проблема использования DateTime.Now или DateTime.UtcNow заключается в том, что их точность фиксирована. Как было сказано выше

1 tick = 100 nanoseconds = 0.1 microseconds = 0.0001 milliseconds = 0.0000001 seconds

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

Использование класса Stopwatch

Публичный API класса Stopwatch выглядит следующий образом:

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

Реализация Stopwatch

Класс Stopwatch основан на HPET (High Precision Event Timer, таймер событий высокой точности). Данный таймер был введён фирмой Microsoft, чтобы раз и навсегда поставить точку в проблемах измерения времени. Частота этого таймера (минимум 10 МГц) не меняется во время работы системы. Для каждой системы Windows сама определяет, с помощью каких устройств реализовать этот таймер.

Класс Stopwatch содержит следующие поля:

TicksPerMillisecond — определяет количество DateTime тактов в 1 миллисекунду;
TicksPerSecond — определяет количество DateTime тактов в 1 секунду;

isRunning — определяет, запущен ли текущий экземпляр (вызван ли был метод Start);
startTimeStamp — число тактов на момент запуска;
elapsed — общее число затраченных тактов;

tickFrequency — упрощает перевод тактов Stopwatch в такты DateTime.

Статический конструктор проверяет наличие таймера HPET и в случае его отсутствия частота Stopwatch устанавливается равной частоте DateTime.

Основной сценарий работы данного класса был показан выше: вызов метода Start, метод время которого необходимо измерить, а затем вызов метода Stop.

Реализация метода Start очень проста — он запоминает начальное число тактов:

Следует сказать, что вызов метода Start на уже замеряющем экземпляре ни к чему не приводит.

Аналогично просто устроен метод Stop:

Вызов метода Stop на остановленном экземпляре так же ни к чему не приводит.

Оба метода используют вызов GetTimestamp() — возвращающего количество тактов на момент вызова:

При наличии HPET(таймер событий высокой точности) такты Stopwatch отличаются от тактов DateTime.

на моем компьютере выводит

Использовать такты Stopwatch для создания DateTime или TimeSpan неверно. Запись

по понятным причинам приведет к неправильным результатам.

Чтобы получить такты DateTime, а не Stopwatch нужно воспользоваться свойствами Elapsed и ElapsedMilliseconds или же сделать преобразование вручную. Для преобразования тактов Stopwatch в такты DateTime в классе используется следующий метод:

Код свойств выглядит, как и ожидалось:

Что плохого в использовании Stopwatch?

Примечание к данному классу с MSDN говорит: на многопроцессорном компьютере не имеет значения, на каком из процессоров выполняется поток. Однако, из-за ошибок в BIOS или слое абстрагированного оборудования (HAL), можно получить различные результаты расчета времени на различных процессорах.

Во избежание этого в методе Stop стоит условие if (elapsed

Источник

Измерение времени выполнения Java – кода с помощью секундомера Spring

В этом уроке мы рассмотрим, как измерить время выполнения кода Java для проектов на базе Spring с помощью секундомера и его простого API.

Вступление

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

В многопоточных системах также полезно измерять время выполнения отдельных потоков с или асинхронных задач.

Урок секундомера весны

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

Примечание: Стоит отметить, что Секундомер не является потокобезопасным.

Измерение времени выполнения кода с помощью секундомера

Секундомер принадлежит к ядру util пакета Spring:

Естественно, он также присутствует в spring-boot-starter-web зависимости:

Давайте продолжим и создадим задачу с именем и измерим время выполнения части кода:

Теперь это приводит к:

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Примечание: Если мы используем Секундомер для измерения времени выполнения кода для большого количества интервалов (порядка сотен тысяч или миллионов) – список TaskInfo будет занимать значительную часть вашей рабочей памяти. Вы можете отключить его с помощью:

Вывод

В этом уроке мы рассмотрели класс утилиты Секундомер – ответ Spring на врожденное отсутствие инструментов измерения времени в Java.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *