Как замерить время выполнения метода в Java?
Использование System.nanoTime() для точных замеров
Ваш подход с System.currentTimeMillis() имеет два недостатка:
- Низкое разрешение: точность обычно около 10-15 мс (зависит от ОС и JVM). Для коротких методов (менее 1 мс) вы получите 0.
- Измеряет wall-clock time: учитывает время, когда поток не выполнялся (например, при сборке мусора или переключении контекста).
System.nanoTime(). Он измеряет время с наносекундной точностью (на практике — микросекунды) и использует высокоточный таймер ОС.
Правильный способ: System.nanoTime()
public class TimerExample {
public static void main(String[] args) {
long startTime = System.nanoTime();
// Тестируемый код
double sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += Math.sqrt(i);
}
long endTime = System.nanoTime();
long duration = endTime - startTime; // в наносекундах
System.out.println("Время выполнения: " + duration / 1_000_000.0 + " мс");
System.out.println("Результат: " + sum);
}
}
Важные нюансы при использовании nanoTime()
- Не используйте для сравнения разных потоков:
nanoTime()привязан к конкретному потоку/ядру процессора. Разница между замерами в разных потоках может быть некорректной. - Не вызывайте внутри JIT-компиляции: первый запуск метода может быть медленнее из-за компиляции. Делайте "прогревочный" запуск перед замером.
- Учитывайте погрешность: даже
nanoTime()может иметь джиттер (до нескольких микросекунд). Для коротких методов запускайте их много раз и усредняйте.
Утилитный класс для замеров
public class MethodTimer {
public static void timeMethod(Runnable method, String name) {
// Прогрев JIT
for (int i = 0; i < 10_000; i++) {
method.run();
}
// Замер
long total = 0;
int iterations = 100;
for (int i = 0; i < iterations; i++) {
long start = System.nanoTime();
method.run();
long end = System.nanoTime();
total += (end - start);
}
double avgMs = (total / (double) iterations) / 1_000_000.0;
System.out.printf("%s: среднее %.3f мс%n", name, avgMs);
}
public static void main(String[] args) {
timeMethod(() -> {
double sum = 0;
for (int i = 0; i < 10_000; i++) {
sum += Math.sin(i);
}
}, "sin loop");
}
}
Альтернатива: Java Microbenchmark Harness (JMH)
Для серьезных бенчмарков используйте JMH — стандартную библиотеку от Oracle. Она автоматически:
- Прогревает JIT-компилятор
- Усредняет результаты
- Избегает dead code elimination
- Учитывает погрешности ОС
// Пример JMH (добавьте зависимость в Maven/Gradle)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 10, time = 1)
@Fork(1)
public class MyBenchmark {
@Benchmark
public void testMethod() {
double sum = 0;
for (int i = 0; i < 100_000; i++) {
sum += Math.sqrt(i);
}
}
}
Рекомендации
- Для быстрых замеров в коде —
System.nanoTime()с прогревочным циклом. - Для точных бенчмарков — JMH.
- Избегайте
System.currentTimeMillis()для измерения производительности.
Использование Stopwatch из библиотеки Apache Commons или Guava
Если вы не хотите писать утилитный класс самостоятельно, можно использовать готовые решения из популярных библиотек.
Apache Commons Lang3 — StopWatch
import org.apache.commons.lang3.time.StopWatch;
StopWatch watch = new StopWatch();
watch.start();
// Ваш код
Thread.sleep(100);
watch.stop();
System.out.println("Время: " + watch.getTime() + " мс");
// Или с наносекундами:
System.out.println("Время: " + watch.getNanoTime() + " нс");
Преимущества: простой API, поддержка пауз/возобновления, несколько форматов вывода.
Google Guava — Stopwatch
import com.google.common.base.Stopwatch;
Stopwatch stopwatch = Stopwatch.createStarted();
// Ваш код
Thread.sleep(100);
stopwatch.stop();
System.out.println("Время: " + stopwatch); // выведет "100.0 ms"
Guava-версия более лаконична и автоматически выбирает подходящую единицу измерения (нс, мкс, мс, с).
Дополнительный совет
Для профилирования всего приложения (а не одного метода) используйте встроенный Java Flight Recorder (JFR) или профайлеры типа VisualVM. Они покажут не только время, но и количество вызовов, аллокации памяти и блокировки.
Ответов пока нет.
Войдите чтобы оставить ответ