Накладные расходы времени в Java
При измерении прошедшего времени на низком уровне у меня есть выбор, используя любой из них:
System.currentTimeMillis();
System.nanoTime();
Оба метода реализованы native
. Прежде чем вникать в какой-либо C-код, кто-нибудь знает, есть ли существенные накладные расходы, вызывающие тот или иной? Я имею в виду, если мне действительно не нужна дополнительная точность, от которой можно ожидать, что процессор будет меньше времени?
N.B: Я использую стандартный Java 1.6 JDK, но вопрос может быть применим для любой JRE...
Ответы
Ответ 1
Ответ, помеченный правильно на этой странице, на самом деле неверен. Это недействительный способ написать контрольный показатель из-за исключения JXM-кода (DCE), замены на стеке (OSR), разворачивания циклов и т.д. Только такая инфраструктура, как Oracle MicroMarkmarkmark Framework, может правильно измерить что-то подобное. Прочитайте этот пост, если у вас есть сомнения относительно достоверности таких микро-тестов.
Вот тест JMH для System.currentTimeMillis()
vs System.nanoTime()
:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class NanoBench {
@Benchmark
public long currentTimeMillis() {
return System.currentTimeMillis();
}
@Benchmark
public long nanoTime() {
return System.nanoTime();
}
}
И вот результаты (на Intel Core i5):
Benchmark Mode Samples Mean Mean err Units
c.z.h.b.NanoBench.currentTimeMillis avgt 16 122.976 1.748 ns/op
c.z.h.b.NanoBench.nanoTime avgt 16 117.948 3.075 ns/op
Что показывает, что System.nanoTime()
немного быстрее при ~ 118 нс на вызов по сравнению с ~ 123ns. Однако также ясно, что, как только средняя ошибка принимается во внимание, между ними очень мало различий. Результаты также могут варьироваться в зависимости от операционной системы. Но общий вывод должен состоять в том, что они по существу эквивалентны с точки зрения накладных расходов.
UPDATE 2015/08/25: Хотя этот ответ ближе к правилу, большинство из них, используя JMH для измерения, все еще не правильно. Измерение чего-то типа System.nanoTime()
само по себе является особым видом скрученного бенчмаркинга. Ответ и окончательная статья здесь.
Ответ 2
Я не верю, что вам нужно беспокоиться о накладных расходах. Это настолько минимально, что оно едва измеримо. Вот быстрый микро-бенчмарк обоих:
for (int j = 0; j < 5; j++) {
long time = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
long x = System.currentTimeMillis();
}
System.out.println((System.nanoTime() - time) + "ns per million");
time = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
long x = System.nanoTime();
}
System.out.println((System.nanoTime() - time) + "ns per million");
System.out.println();
}
И последний результат:
14297079ns per million
29206842ns per million
Похоже, что System.currentTimeMillis()
в два раза быстрее, чем System.nanoTime()
. Однако 29ns будет намного короче, чем все, что вы будете измерять во всяком случае. Я бы выбрал System.nanoTime()
для точности и точности, поскольку он не связан с часами.
Ответ 3
Вы всегда должны использовать System.nanoTime()
для измерения того, сколько времени требуется для запуска. Это не просто вопрос наносекундной точности, System.currentTimeMillis()
- это "настенные часы", а System.nanoTime()
предназначен для синхронизации вещей и не имеет причуд "реального времени", который делает другой. Из Javadoc System.nanoTime()
:
Этот метод может использоваться только для измерения прошедшего времени и не имеет отношения к какому-либо другому понятию времени системы или настенного времени.
Ответ 4
System.currentTimeMillis()
обычно очень быстрый (afaik 5-6 cpu cycles, но я не знаю, где я это уже читал), но разрешение зависит от разных платформ.
Итак, если вам нужна высокая точность, заходите на nanoTime()
, если вас беспокоит накладные расходы для currentTimeMillis()
.
Ответ 5
Если у вас есть время, посмотрите этот разговор Клиффом Кликом, он говорит о цене System.currentTimeMillis
, а также о других вещах.
Ответ 6
Принятый ответ на этот вопрос действительно неверен. Альтернативный ответ, предоставленный @brettw, хорош, но тем не менее освещен деталями.
Для полного изучения этого предмета и реальной стоимости этих вызовов см. https://shipilev.net/blog/2014/nanotrusting-nanotime/
Чтобы ответить на заданный вопрос:
Кто-нибудь знает, есть ли существенные накладные расходы, вызывающие тот или иной?
- Накладные расходы при вызове
System#nanoTime
составляют от 15 до 30 наносекунд за вызов.
- Значение, сообщенное
nanoTime
, его разрешение, изменяется только один раз за 30 наносекунд
Это означает, что если вы пытаетесь сделать миллион запросов в секунду, вызов nanoTime
означает, что вы фактически теряете огромный кусок второго вызова nanoTime
. Для таких случаев использования рассмотрите либо запросы на измерение со стороны клиента, чтобы вы не попадали в скоординированное упущение, измерение глубины очереди также является хорошим индикатором.
Если вы не пытаетесь втиснуть столько работы, сколько можете, за одну секунду, чем nanoTime
не имеет значения, но координированное упущение все еще остается фактором.
Наконец, для полноты currentTimeMillis
нельзя использовать независимо от его стоимости. Это связано с тем, что он не гарантирует переход между двумя вызовами. Особенно на сервере с NTP, currentTimeMillis
постоянно перемещается. Не говоря уже о том, что большинство вещей, измеренных компьютером, не занимают полных миллисекунд.
Ответ 7
На теоретическом уровне для виртуальной машины, использующей собственные потоки, и сидит на современной упреждающей операционной системе, currentTimeMillis может быть реализована для чтения только один раз за один раз. Предположительно, реализации nanoTime не пожертвуют точностью.