Как рассчитать время, затраченное на выполнение метода в Java?
Вы можете взять моментальные снимки времени до и после, затем повторить эксперименты несколько раз, чтобы усреднить результаты. Существуют также профилировщики, которые могут сделать это за вас.
Из книги "Производительность платформы Java: стратегии и тактика":
С System.currentTimeMillis()
С классом StopWatch
Вы можете использовать этот класс StopWatch и вызывать start() и stop до и после метода.
Профилировщик NetBeans:
Application Performance Application
Профили производительности процессор уровня производительность (время выполнения). Ты можешь выбрать профиль для всего приложения или части приложение.
Чтобы быть более точным, я использовал бы метод nanoTime() , а не currentTimeMillis() :
В Java 8 (выходной формат ISO-8601):
С помощью этого вы можете рассчитать время своего метода, выполнив:
Если вы разрабатываете приложения для Android, вы должны попробовать TimingLogger класс.
Взгляните на эти статьи, описывающие использование вспомогательного класса TimingLogger :
Возможно, вам стоит подумать об аспектно-ориентированном программировании. Вы не хотите засорять свой код таймингами. Вы хотите, чтобы их можно было отключить и декларативно.
Если вы используете Spring, просмотрите класс MethodInterceptor.
Если вы в настоящее время пишете приложение, то ответ на использование System.currentTimeMillis или System.nanoTime служат цели, указанной выше.
Но если вы уже написали код и не хотите его менять, лучше использовать перехватчики методов Spring. Например, ваша служба:
Чтобы избежать изменения службы, вы можете написать свой собственный метод перехватчика:
Также существуют API-интерфейсы с открытым исходным кодом, доступные для Java, например. BTrace. или профилировщиком Netbeans, как было предложено выше @bakkal и @Saikikos. Спасибо.
Как предлагалось, nanoTime() очень точна на коротких временных масштабах. Когда требуется эта точность, вам нужно позаботиться о том, что вы действительно измеряете. Особенно, чтобы не измерить сам вызов нанотима
Кстати, вы будете измерять разные значения для одного и того же вызова из-за
- другая загрузка вашего компьютера (фон, сеть, перемещение мыши, прерывания, переключение задач, потоки).
- заполнение кеша (холодное, теплое)
- jit компиляция (без оптимизации, производительности, вызванной запуском компилятора, повышение производительности из-за компилятора (но иногда код с jit медленнее, чем без!))
Нанотиме на самом деле не очень полезно в течение прошедшего времени, потому что оно значительно отличается от текущегоTimeMillis. Кроме того, nanotime имеет тенденцию обеспечивать чрезмерную точность за счет точности. Поэтому он крайне несовместим и нуждается в уточнении.
Для любого процесса измерения времени currentTimeMillis (хотя и почти так же плохо) делает лучше с точки зрения точности и точности балансировки.
Посмотрите другие вопросы по метке java или Задайте вопрос
Как получить время выполнения метода? Есть ли класс утилиты таймера для таких вещей, как время, сколько времени занимает задача и т. д.?
большинство поисков в Google возвращают результаты для таймеров, которые планируют потоки и задачи, что не то, что я хочу.
30 ответов
Я иду с простым ответом. Работать на меня.
он работает довольно хорошо. Разрешение, очевидно, только до миллисекунды, вы можете сделать лучше с системой.nanoTime(). Существуют некоторые ограничения для обоих (срезы расписания операционной системы и т. д.) но это работает довольно хорошо.
среднее значение через пару прогонов (чем больше, тем лучше), и вы получите достойную идею.
ребята! Никто не упоминал гуавы способ сделать это (что, бесспорно, офигенно):
хорошая вещь, что секундомер.toString () выполняет хорошую работу по выбору единиц времени для измерения. Т. е. если значение мало, оно выведет 38 НС, если оно длинное, то оно покажет 5м 3С
Примечание: Google Guava требует Java 1.6+
используя МиГ и продолжительность из нового API Java 8,
используйте профилировщик (Jprofiler, Netbeans Profiler, Visual VM, Eclipse Profiler и т. д.). Вы получите наиболее точные результаты и наименее навязчивы. Они используют встроенный механизм JVM для профилирования, который также может дать вам дополнительную информацию, такую как трассировки стека, пути выполнения и более полные результаты, если это необходимо.
при использовании полностью интегрированного профилировщика, это faily тривиально профилировать метод. Щелкните правой кнопкой мыши, Profiler — > добавить в корневые методы. Запустите профайлер просто как будто вы делали тестовый запуск или отладчик.
собрали все возможные варианты в одном месте.
Удобочитаемом
Гуава: Google секундомер JAR " объектом секундомера является измеряет прошедшее время в наносекундах.
Apache Commons Lang JAR "секундомер предоставляет удобный API для таймингов.
Джодавремени
JAVA дата время API от Java 8 " A продолжительность
Это, вероятно, не то, что вы хотели, чтобы я сказал, но это хорошее использование AOP. Взбейте прокси-перехватчик вокруг вашего метода и сделайте время там.
что, почему и как AOP довольно выходит за рамки этого ответа, к сожалению, но это то, как я, вероятно, это сделаю.
Edit:вот ссылка Весна AOP, чтобы вы начали, если вы заинтересованы. Это самая доступная реализация AOP, с которой сталкивается Iive Ява.
кроме того, учитывая очень простые предложения всех остальных, я должен добавить, что AOP-это когда вы не хотите, чтобы такие вещи, как время, вторглись в ваш код. Но во многих случаях, такой простой и легкий подход-это нормально.
System.currentTimeMillis(); не является хорошим подходом для измерения производительности ваших алгоритмов. Он измеряет общее время, которое вы испытываете в качестве пользователя, наблюдающего за экраном компьютера. Он также включает в себя время, затрачиваемое на все остальное работает на вашем компьютере в фоновом режиме. Это может иметь огромное значение, если на вашей рабочей станции работает много программ.
правильный подход использует java.lang.management пакета.
- "время" — это время, затраченное на выполнение кода приложения.
- "системное время" — это время, затраченное на выполнение кода ОС от имени вашего приложения (например, для ввода-вывода).
getCpuTime() метод дает вам сумму тех:
С Java 8 вы также можете сделать что-то подобное с каждым нормальным методы:
С помощью этого methode вы можете легко измерять время в любом месте вашего кода, не нарушая его. В этом простом примере я просто печатаю время. Можете ли вы добавить переключатель для TimeIt, например, только для печати времени в DebugMode или что-то еще.
Если вы работаете с функции вы можете сделать что-то вроде это:
также мы можем использовать класс секундомера Apache commons Для измерения времени.
просто небольшой поворот, если вы не используете инструменты и хотите использовать методы с низким временем выполнения: выполняйте его много раз, каждый раз удваивая количество раз, пока не достигнете секунды или около того. Таким образом, время вызова системы.nanoTime и так далее, ни точность системы.nanoTime сильно влияет на результат.
конечно, предостережения об использовании настенных часов применяются: влияния JIT-компиляции, нескольких потоков / процессов и т. д. Таким образом, вы необходимо сначала выполнить метод большое раз сначала, так что компилятор JIT выполняет свою работу, а затем повторите этот тест несколько раз и возьмите наименьшее время выполнения.
для этой цели мы используем аннотации AspectJ и Java. Если нам нужно знать время выполнения метода, мы просто аннотируем его. Более продвинутая версия может использовать собственный уровень журнала, который может быть включен и отключен во время выполнения.
очень хороший код.
можно использовать Perf4j. Очень классная утилита. Использование просто
более подробную информацию можно найти в Руководство Разработчика
изменить: кажется, умер
использование AOP / AspectJ и @Loggable аннотации jcabi-аспекты вы можете сделать это легкий и компактный:
каждый вызов этого метода будет отправлен на объект регистрации SLF4J с DEBUG уровень ведения журнала. И каждое сообщение журнала будет включать время выполнения.
Я в основном делаю вариации этого, но, учитывая, как работает компиляция hotspot, если вы хотите получить точные результаты, вам нужно выбросить первые несколько измерений и убедиться, что вы используете метод в реальном мире (прочитайте конкретное приложение).
Если JIT решит скомпилировать его, ваши номера будут сильно отличаться. так что просто будьте в курсе
есть несколько способов сделать это. Обычно я возвращаюсь к использованию чего-то подобного:
или то же самое с системой.nanoTime();
для чего-то большего на стороне бенчмаркинга вещей, кажется, также есть этот:http://jetm.void.fm/ Никогда не пробовал.
Если вы хотите настенные часы время
Как сказал "skaffman", используйте AOP или вы можете использовать байт-код времени выполнения, так же, как инструменты покрытия метода модульного теста используют для прозрачного добавления информации о времени к вызываемым методам.
Если вам в конечном итоге удастся сделать то, что вы наметили, pls. поделитесь им с сообществом здесь с вашей задачей ant / jars.
Spring предоставляет служебный класс org.springframework.утиль.Секундомер, согласно JavaDoc:
простой секундомер, позволяющ для времени нескольких задач, подвергая действию общее время выполнения и время выполнения для каждой именованной задачи.
С Аспектами:
Я изменил код с правильного ответа, чтобы получить результат в секундах:
вы можете попробовать этот способ, если просто хотите знать время.
Ok, это простой класс, который будет использоваться для простого простого синхронизации ваших функций. Ниже приведен пример.
пример вывода на консоль:
в Java 8 новый класс с именем это. Согласно doc:
Instant представляет собой начало наносекунды на временной линии. Этот класс полезен для создания метки времени для представления машинного времени. Диапазон мгновения требует хранения числа больше, чем a длинный. Для этого класс хранит длинное представление epoch-секунды и int, представляющий наносекунду секунды, которая будет всегда будет между 0 и 999 999 999. Этот epoch-измеряются секунды от стандартной эпохи Java 1970-01-01T00:00:00Z где мгновения после эпохи имеют положительные значения, а более ранние мгновения имеют отрицательное значение. Для эпох-второй и наносекундной частей a большее значение всегда является более поздним по времени, чем меньшее значение.
Это можно использовать как:
можно использовать показатели библиотека, которая предоставляет различные измерительные приборы. Добавить зависимость:
и настройте его для своей среды.
методы могут быть аннотированы с @Timed:
или кусок кода, обернутый таймер:
агрегированные метрики могут экспортироваться в консоль, JMX, CSV или другие.
@Timed пример вывода метрик:
System.nanoTime() — довольно точная системная утилита для измерения времени выполнения. Но будьте осторожны, если вы работаете в режиме упреждающего планировщика (по умолчанию), эта утилита фактически измеряет время настенных часов, а не время процессора. Таким образом можно заметить различные значения времени выполнения от запуска до запуска, в зависимости от загрузки системы. Если вы ищете время процессора, я думаю, что запуск вашей программы в режиме реального времени сделает трюк. Вы должны использовать RT linux. ссылка:программирование в реальном времени с В Linux
измерения производительности на моей машине
- система.nanoTime() : 750ns
- система.currentTimeMillis() : 18ns
Как уже упоминалось, система.считается, что nanoTime() измеряет прошедшее время. Просто имейте в виду стоимость, если используется insied цикл или тому подобное.
Во многих проектах требуется посчитать время, которое затратил тот или иной метод. Для этого можно вручную сохранять значение 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;
@Target()
@Retention(RetentionPolicy.SOURCE)
public @ interface Time <
public enum TimeInterval < MILLISECOND, NANOSECOND >;
annotations.time.Time.TimeInterval interval() default annotations.time.Time.TimeInterval.MILLISECOND;
String format() default "Elapsed %s" ;
>
* This source code was highlighted with Source Code Highlighter .
Поле 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 void init(ProcessingEnvironment procEnv) <
super.init(procEnv);
this .javacProcessingEnv = (JavacProcessingEnvironment) procEnv;
this .maker = TreeMaker.instance(javacProcessingEnv.getContext());
>
@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);
if (annotation != null ) <
// Выбираем все элементы, у которых стоит наша аннотация
final Set methods = roundEnv.getElementsAnnotatedWith(annotation);
JavacElements utils = javacProcessingEnv.getElementUtils();
for (final Element m : methods) <
Time time = m.getAnnotation(Time. class );
if (time != null ) <
JCTree blockNode = utils.getTree(m);
// Нам нужны только описания методов
if (blockNode instanceof JCMethodDecl) <
// Получаем содержимое метода
final List statements = ((JCMethodDecl) blockNode).body.stats;
// Новое тело метода
List newStatements = List .nil();
// Добавляем в начало метода сохранение текущего времени
JCVariableDecl var = makeTimeStartVar(maker, utils, time);
newStatements = newStatements.append( var );
// Создаём тело блока try, копируем в него оригинальное содержимое метода
List tryBlock = List .nil();
for (JCStatement statement : statements) <
tryBlock = tryBlock.append(statement);
>
// Создаём тело блока finally, добавляем в него вывод затраченного времени
JCBlock finalizer = makePrintBlock(maker, utils, time, var );
JCStatement stat = maker.Try(maker.Block(0, tryBlock), List . nil(), finalizer);
newStatements = newStatements.append(stat);
// Заменяем старый код метода на новый
((JCMethodDecl) blockNode).body.stats = newStatements;
>
>
>
private JCExpression makeCurrentTime(TreeMaker maker, JavacElements utils, Time time) <
// Создаём вызов System.nanoTime или System.currentTimeMillis
JCExpression exp = maker. >"System" ));
String methodName;
switch (time.interval()) <
case NANOSECOND:
methodName = "nanoTime" ;
break ;
default :
methodName = "currentTimeMillis" ;
break ;
>
exp = maker.Select(exp, utils.getName(methodName));
return maker.Apply( List . nil(), exp, List . nil());
>
protected JCVariableDecl makeTimeStartVar(TreeMaker maker, JavacElements utils, Time time) <
// Создаём финальную переменную для хранения времени старта. Имя переменной в виде time_start_
JCExpression currentTime = makeCurrentTime(maker, utils, time);
String fieldName = fieldName = "time_start_" + ( int ) ( Math .random() * 10000);
return maker.VarDef(maker.Modifiers(Flags.FINAL), utils.getName(fieldName), maker.TypeIdent(TypeTags.LONG), currentTime);
>
protected JCBlock makePrintBlock(TreeMaker maker, JavacElements utils, Time time, JCVariableDecl var ) <
// Создаём вызов System.out.println
JCExpression printlnExpression = maker. >"System" ));
printlnExpression = maker.Select(printlnExpression, utils.getName( "out" ));
printlnExpression = maker.Select(printlnExpression, utils.getName( "println" ));
// Создаём блок вычисления затраченного времени (currentTime — startTime)
JCExpression currentTime = makeCurrentTime(maker, utils, time);
JCExpression elapsedTime = maker.Binary(JCTree.MINUS, currentTime, maker. >var .name));
// Форматируем результат
JCExpression formatExpression = maker. >"String" ));
formatExpression = maker.Select(formatExpression, utils.getName( "format" ));
// Собираем все кусочки вместе
List formatArgs = List .nil();
formatArgs.append(maker.Literal(time.format()));
formatArgs.append(elapsedTime);
JCExpression format = maker.Apply( List . nil(), formatExpression, formatArgs);
List printlnArgs = List .nil();
printlnArgs.append(format);
JCExpression print = maker.Apply( List . nil(), printlnExpression, printlnArgs);
JCExpressionStatement stmt = maker.Exec(print);
List stmts = List .nil();
stmts.append(stmt);
return maker.Block(0, stmts);
>
>
* This source code was highlighted with Source Code Highlighter .
Для того чтобы компилятор java использовал наш управляющий класс, нужно создать файл META-INF/javax.annotation.processing.Processor, в котором должна быть прописана следующая строка:
annotations.time.TimeAnnotationProcessor
После этого собираем все наши файлы в annotations.jar и добавляем его в classpath к любому проекту.
Теперь, чтобы посчитать время выполнения метода, достаточно добавить к этому методу аннотацию Time и после его выполнения в консоль будет выведено затраченное методом время:
@Time(format= "method time: %s ms" )
public void start() <
Thread.sleep(1000);
>* This source code was highlighted with Source Code Highlighter .
В результате в консоли увидим:
method time: 1000 ms






