2017-02-11 107 views
1

После прочтения подобных тем в разделе «Переполнение стека», я написал следующий код, чтобы получить представление о том, насколько согласуется System.nanoTime().Почему первый вызов метода всегда занимает самое длинное?

Он просто вызывает System.nanoTime() до и после вызова метода на пустую функцию пустоты, записывая прошедшее время в процессе. Однако, как вы можете видеть в результатах, первый вызов всегда занимает самое большое время. Что является причиной этого?

public class Test { 
    public static void main(String[] args) { 
     for(int i = 0; i < 10; i++) { 
      double start = System.nanoTime(); 
      foo(); 
      double end = System.nanoTime(); 
      double diff = end - start; 
      System.out.println("Diff: " + diff); 
     } 
    } 

    public static void foo() { 

    } 
} 

Результаты:

Diff: 2765.0 
Diff: 509.0 
Diff: 236.0 
Diff: 238.0 
Diff: 230.0 
Diff: 539.0 
Diff: 359.0 
Diff: 356.0 
Diff: 380.0 
Diff: 353.0 

Обратите внимание, что я прочитал этот вопрос: Why does first call to java.io.File.createTempFile(String,String,File) take 5 seconds on Citrix?

Кроме того, эта ссылка полезно для дальнейшего использования, но не обязательно отвечать на мой конкретный вопрос: How do I write a correct micro-benchmark in Java?

+1

Одним словом: "caches" (на всех слоях). Как только кэш задействован, делать то же самое гораздо быстрее во второй раз. Кроме того, разница в 2 микросекунды на самом деле ничего не получается, и почему вы конвертируете «nanoTime» в число с плавающей запятой? – Thilo

+0

Этот метод действительно пуст? Может быть, JVM показывает это и полностью его оптимизирует? Проделайте два следующих действия: a) измерьте разницу nanoTime без какого-либо вызова кода или метода между ними. B) поместите небольшой расчет в этот метод 'foo'. – Thilo

+0

Microbenchmarking на JVM сложно, потому что [JIT-компилятор] (http://stackoverflow.com/questions/95635/what-does-a-just-in-time-jit-compiler-do) может тонко изменить, как ваш код работает после того, как код работает некоторое время. Правильный способ сделать это - использовать настоящую базовую платформу, например [jmh] (http://openjdk.java.net/projects/code-tools/jmh/) –

ответ

3

JVM выполняет разрешение класса (см. JVMS 5.4.3) лениво. В вашем случае символьная ссылка на foo в постоянном пуле разрешается при первом выполнении байтового кода invokestatic, то есть при первом вызове метода. Очевидно, что требуется больше времени, чем просто выполнение уже разрешенного байт-кода.