Подсчёт времени выполнения метода через аннотацию
Во многих проектах требуется посчитать время, которое затратил тот или иной метод. Для этого можно вручную сохранять значение 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.Ident(utils.getName( «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.Ident(utils.getName( «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.Ident( var .name));
// Форматируем результат
JCExpression formatExpression = maker.Ident(utils.getName( «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
UPD: добавил немного комментариев в код
UPD2: Так же можно переработать пример, чтобы он выводил время затраченное методом в System.err только если оно больше заданного в параметрах аннотации. Это могло бы быть полезно для production серверов, где пользоваться профайлером не всегда удобно.
Как засечь время выполнения цикла или программы или какого-то оператора?
Засечь время выполнения (в ms) определённого кода или части кода
Доброго времени суток всем. Хотелось бы поинтересоваться у знающих людей. Как можно засеч время.
Какой функцией можно засечь время выполнения программы
Здравствуйте. подскажите какой функцией можно засечь время выполнения программы и вывести его в.
Как запретить удаление Папки или Файла на время выполнения программы
Собственно сабж. Нужно запретить удаление папки, причем совершенно левой, как это сделать кто.
Как засечь время выполнения функции?
Упражнение 3.1 K&R как новичку без серьезных описаний и дополнений кода засечь время выполнения.
237 / 236 / 72
Регистрация: 02.07.2013
Сообщений: 881
1 2 3
long startTime = System.nanoTime(); // . the code being measured . long estimatedTime = System.nanoTime() - startTime;
Регистрация: 08.04.2013
Сообщений: 138
Wado-Ru, это в каких единицах измеряется?
237 / 236 / 72
Регистрация: 02.07.2013
Сообщений: 881
currentTimeMillis() Returns the current time in milliseconds.
nanoTime() Returns the current value of the most precise available system timer, in nanoseconds
это все из документации по ссылке
Регистрация: 28.04.2014
Сообщений: 31
К сожалению, считать время выполнения таких вещей очень и очень сложно. И, разумеется, одним currentTimeMillis() или nanoTime() здесь не отделаться.
Во-первых, вы знаете в каком режиме у вас будет работать метод в момент подсчета? Если в интерпретируемом режиме (выполнение байт-кода), то у вас будут одни цифры. Если в скомпилированном варианте (нативный код), то совсем другие. Если начнут компилироваться какие-то зависимые методы, то третьи. И т.д..
Во-вторых, а вы уверены, что ваш цикл реально будет выполнятся? Может быть в боевом окружении он работает, а в тестовом компилятор, проведя escape-analysis, его тупо игнорирует?
В-третьих, у самих методов получения времени есть свои погрешности. Например, на линуксах они порядка 30-50 наносекунд. На Виндах могут доходить до тысяч наносекунд. Это озаначает, что невозможо более-менее точно замерить что-то, что длиться меньше указанного времени (по аналогии с длиной волны света и разрешающей способности светового микроскопа).
К счастью, за нас уже побеспокоились об этом — JMH. На данные момент это единственный инструмент для достоверного проведения микробенчмарков. Но одного его недостаточно. Надо помнить про погрешности, про прогрев, про богус-фрагменты, и т.д.. JMH дает возможность легко ими управлять, но если вы про них не знаете, у вас все равно ничего не получится.
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
Помогаю со студенческими работами здесь
Засечь время выполнения поиска
Создается массив случайных чисел, сортируется, и затем проводится поиск по этому массиву. Для.
Засечь время выполнения пирамидальной сортировки
мне нужно засечь время выполнения алгоритма сортировок, и у меня не выходит только с одной — с.
Нужно засечь время выполнения процедуры
писал вот код form1.timer.enabled:=true; search; form1.timer.enabled:=false;Код таймера.
Окно программы во время выполнения цикла
Пока выполняется цикл в программе окно становится не доступным. Ни свернуть не переместить и тд.
Сколько времени выполнялась программа (Java)
Выдает вот такое значение : 1482923121138 Как не пытался конвертировать и где только не гуглил,выход так и не нашел. Мне нужно преобразовать полученное значение в секунды. Т.е. вывести на экран время выполнения программы.
Отслеживать
задан 28 дек 2016 в 11:06
Marat Zimnurov Marat Zimnurov
554 3 3 золотых знака 8 8 серебряных знаков 28 28 бронзовых знаков
7 ответов 7
Сортировка: Сброс на вариант по умолчанию
System.currentTimeMillis() — возвращает количество миллисекунд прошедших с полуночи 1 января 1970 года, это называется UNIX-время.
Чтобы посчитать сколько времени выполнялся какой-то кусок кода, нужно посчитать разницу, т.е. что-то типа:
long time = System.currentTimeMillis(); someMethod(); // some code System.out.println(System.currentTimeMillis() - time);
Так же часто код выполняется очень быстро, тогда надо заменить за сколько выполнится большое количество повторений этого кода.
Или не всегда за стабильное время, в этом случае надо провести несколько замеров и посчитать среднее арифметическое.
Таймер — классы Timer и TimerTask
Классы Timer и TimerTask из пакета java.util позволяют планировать запуск задания на определённое время в будущем. Вы можете создать поток, выполняющий в фоновом режиме и ожидающий заданное время. Когда время истечёт, задача, связанная с этим потоком, будет запущена. С помощью параметров можно запланировать задачу на повторяющий запуск либо на запуск по определённой дате. Вам не нужно создавать поток с помощью класса Thread, так как таймер упрощает эту задачу.
Учитывайте обстоятельство, что таймер выполняется в своём потоке и не должен задействовать UI-элементы, которые выполняются в своём потоке. Для решения этой проблемы можете использовать метод runOnUiThread() для обновления данных у компонентов.
Классы Timer и TimerTask работают в связке. Класс Timer используется для планирования выполнения задачи. Запланированная к выполнению задача должна быть экземпляром класса TimerTask. Вы сначала создаёте объект класса TimerTask, а затем планируете его запуск с помощью класса Timer.
Класс TimerTask реализует интерфейс Runnable и может быть использован для создания потока выполнения.
В классе TimerTask имеется абстрактный метод run(), который следует переопределить. Метод должен содержать исполняемый код.
Метод cancel() прерывает задание и возвращает значение true, если выполнение задания прервано.
Метод scheduleExecutionTime() возвращает время, на которое последний раз планировался запуск задания.
Как только задача создана, она планируется для выполнения объектом класса Timer.
Методы класса Timer:
- void cancel() — прерывает поток таймера
- int purge() — удаляет прерванные задания из очереди таймера
- void schedule (TimerTask task, long delay) — задание task планируется к выполнению через период в миллисекундах, переданный в параметре delay
- void schedule (TimerTask task, long delay, long period) — задание task планируется к выполнению через период в миллисекундах, переданный в параметре delay. Затем задание повторяется повторно периодически — каждые period миллисекунд
- void schedule (TimerTask task, Date when) — задание task планируется на время, указанное в параметре when
- void schedule(TimerTask task, Date when, long period) — задание task планируется на время, указанное в параметре when. Затем задание выполняется повторно периодически — каждые period миллисекунд
- void scheduleAtFixedRate (TimerTask task, long delay, long period) — задание task планируется к выполнению через период в миллисекундах, переданный в параметре delay. Затем задание выполняется повторно периодически — каждые period миллисекунд. Время каждого повтора задаётся относительно первого запуска.
- void scheduleAtFixedRate (TimerTask task, Date when, long period) — задание task планируется к выполнению на время, указанное в параметре when. Задание затем выполняется повторно периодически — каждые period миллисекунд. Время каждого повтора задаётся относительно первого запуска.
Между методами schedule() и scheduleAtFixedRate() есть небольшая разница, которая заключается в разном поведении, которое зависит от стартовой точки запуска. Так второй метод работает как startTime + iterationNumber * delayTime и помнит время запуска. А обычный метод schedule() помнит последнее время выполнения и работает по формуле lastExecutionTime + delayTime. Для быстрых операций это не сильно отличается, а при ресурсоёмких задач разница будет заметна, например, при работе сборщика мусора приложение может притормозить и следующая задача может запуститься чуть позже.
Как только объект класса Timer создан, запуск планируется вызовом его метода schedule() и его родственника (см. выше).
Если вам нужно периодически вызывать какое-нибудь событие, то воспользуйтесь следующим примером:
public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); Timer myTimer; myTimer = new Timer(); myTimer.schedule(new TimerTask() < public void run() < timerTick(); >>, 0, 5000); // каждые 5 секунд > private void timerTick() < this.runOnUiThread(doTask); >private Runnable doTask = new Runnable() < public void run() < Toast toast = Toast.makeText(getApplicationContext(), "Мяу!", Toast.LENGTH_SHORT); toast.show(); >>;
Через каждые пять секунд будет появляться всплывающее сообщение.
Запускаем таймер
Напишем другой пример. Подготовим разметку.
Будем использовать два варианта таймера — одиночное и периодическое срабатывание.
package ru.alexanderklimov.timer; // Если этот код работает, его написал Александр Климов, // а если нет, то не знаю, кто его писал. import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.TextView; public class MainActivity extends Activity < private CheckBox mSingleShotCheckBox; private Button mStartButton, mCancelButton; private TextView mCounterTextView; private Timer mTimer; private MyTimerTask mMyTimerTask; @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSingleShotCheckBox = findViewById(R.id.checkBoxSingleShot); mStartButton = findViewById(R.id.buttonStart); mCancelButton = findViewById(R.id.buttonCancel); mCounterTextView = findViewById(R.id.textViewCounter); mStartButton.setOnClickListener(new OnClickListener() < @Override public void onClick(View arg0) < if (mTimer != null) < mTimer.cancel(); >// re-schedule timer here // otherwise, IllegalStateException of // "TimerTask is scheduled already" // will be thrown mTimer = new Timer(); mMyTimerTask = new MyTimerTask(); if (mSingleShotCheckBox.isChecked()) < // singleshot delay 1000 ms mTimer.schedule(mMyTimerTask, 1000); >else < // delay 1000ms, repeat in 5000ms mTimer.schedule(mMyTimerTask, 1000, 5000); >> >); mCancelButton.setOnClickListener(new OnClickListener() < @Override public void onClick(View v) < if (mTimer != null) < mTimer.cancel(); mTimer = null; >> >); > class MyTimerTask extends TimerTask < @Override public void run() < Calendar calendar = Calendar.getInstance(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "dd:MMMM:yyyy HH:mm:ss a", Locale.getDefault()); final String strDate = simpleDateFormat.format(calendar.getTime()); runOnUiThread(new Runnable() < @Override public void run() < mCounterTextView.setText(strDate); >>); > > >
Генерируем случайные показания
Допустим нам нужны ежедневные показания термометра. Но мы не можем ждать весь день при написании программ. Поэтому мы можем случайным образом создавать показания с небольшим интервалом и проверить поведение приложения.
Для удобства создадим отдельный класс-утилиту.
package ru.alexanderklimov.supercat; import java.util.Random; class Utils < private static final Random RANDOM = new Random(); static int randInt(int min, int max) < return RANDOM.nextInt((max - min) + 1) + min; >>
Создадим в классе активности метод для генерации значений и вызовем в onCreate().
package ru.alexanderklimov.supercat; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import java.util.Timer; import java.util.TimerTask; public class MainActivity extends AppCompatActivity < @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setValues(); >private void setValues() < Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() < @Override public void run() < final float value = Utils.randInt(-10, 35); runOnUiThread(new Runnable() < @Override public void run() < Log.i("Info", "Value: " + value); >>); > >, 0, 3500); > >