Накладные расходы времени в 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().

Ответ 6

Принятый ответ на этот вопрос действительно неверен. Альтернативный ответ, предоставленный @brettw, хорош, но тем не менее освещен деталями.

Для полного изучения этого предмета и реальной стоимости этих вызовов см. https://shipilev.net/blog/2014/nanotrusting-nanotime/

Чтобы ответить на заданный вопрос:

Кто-нибудь знает, есть ли существенные накладные расходы, вызывающие тот или иной?

  • Накладные расходы при вызове System#nanoTime составляют от 15 до 30 наносекунд за вызов.
  • Значение, сообщенное nanoTime, его разрешение, изменяется только один раз за 30 наносекунд

Это означает, что если вы пытаетесь сделать миллион запросов в секунду, вызов nanoTime означает, что вы фактически теряете огромный кусок второго вызова nanoTime. Для таких случаев использования рассмотрите либо запросы на измерение со стороны клиента, чтобы вы не попадали в скоординированное упущение, измерение глубины очереди также является хорошим индикатором.

Если вы не пытаетесь втиснуть столько работы, сколько можете, за одну секунду, чем nanoTime не имеет значения, но координированное упущение все еще остается фактором.

Наконец, для полноты currentTimeMillis нельзя использовать независимо от его стоимости. Это связано с тем, что он не гарантирует переход между двумя вызовами. Особенно на сервере с NTP, currentTimeMillis постоянно перемещается. Не говоря уже о том, что большинство вещей, измеренных компьютером, не занимают полных миллисекунд.

Ответ 7

На теоретическом уровне для виртуальной машины, использующей собственные потоки, и сидит на современной упреждающей операционной системе, currentTimeMillis может быть реализована для чтения только один раз за один раз. Предположительно, реализации nanoTime не пожертвуют точностью.