2015-07-18 2 views
2

Я хочу проверить, какой из двух методов выполняется быстрее, однако в зависимости от того, какой из них я запускаю первым, этот метод всегда работает медленнее. Я подозреваю, что есть механизм кэширования, который использует Eclipse, что ускоряет выполнение второго метода.Проверка скорости выполнения кода с помощью Java/Eclipse

private static int method1(int n) { 
    int result = 0; 
    for (int i = 0; i < n + 1; ++i) { 
     result += i; 
    } 
    return result; 
} 

private static int method2(int n) { 
    int result = 0; 
    int i = 0; 
    while (i < n + 1) { 
     result += i++; 
    } 
    return result; 
} 

Это основная функция, которую я использую для проверки разницы во времени.

long start, end; 

start = new Date().getTime(); 
for (int i = 0; i < 100000; ++i) { 
    method1(i); 
} 
end = new Date().getTime(); 
System.out.println(end - start); // 47 

start = new Date().getTime(); 
for (int i = 0; i < 100000; ++i) { 
    method2(i); 
} 
end = new Date().getTime(); 
System.out.println(end - start); // 32 
+4

Написание микро-теста на Java требует дополнительных усилий. Посмотрите эту тему: http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java для некоторого понимания области. –

+1

Имейте в виду, что JVM всегда нуждается в разминке, для некоторых начальных исполнений результирующие времена не будут стоить внимания. –

+0

Так звучит, что этот подход не рекомендуется, правильно? –

ответ

3

Используя jmh, я получаю следующие результаты (при различных значениях N), оценка в наносекундах на вызов метода (меньше = лучше):

Benchmark       (n) Mode Samples   Score   Error Units 
c.a.p.SO31495089.method1    1 avgt  10   2.149  0.027 ns/op 
c.a.p.SO31495089.method1  100000 avgt  10  34626.763  441.915 ns/op 
c.a.p.SO31495089.method1 1000000000 avgt  10 322506247.405 6774340.047 ns/op 
c.a.p.SO31495089.method2    1 avgt  10   2.159  0.028 ns/op 
c.a.p.SO31495089.method2  100000 avgt  10  34581.273  571.416 ns/op 
c.a.p.SO31495089.method2 1000000000 avgt  10 320011679.005 4049907.844 ns/op 

Второй прогон:

Benchmark       (n) Mode Samples   Score   Error Units 
c.a.p.SO31495089.method1    1 avgt  10   2.164  0.029 ns/op 
c.a.p.SO31495089.method1  100000 avgt  10  34706.194  365.189 ns/op 
c.a.p.SO31495089.method1 1000000000 avgt  10 320269697.300 1696038.683 ns/op 
c.a.p.SO31495089.method2    1 avgt  10   2.160  0.040 ns/op 
c.a.p.SO31495089.method2  100000 avgt  10  34627.163  325.981 ns/op 
c.a.p.SO31495089.method2 1000000000 avgt  10 320698252.840 2332275.430 ns/op 

Нижняя линия: эти два метода выполняют аналогично, как и ожидалось (разница во времени выполнения меньше, чем погрешность измерения).

+0

Прохладный приятель, я также добавил анализ байтового кода и дополнительные комментарии для решения вашей проблемы. – hagrawal

0

В то время как для и для петли аналогичная производительность.

Может быть, это похоже пост поможет вам Java For Loop Vs While Loop Performance Difference

+0

О да, я должен был упомянуть ранее, я решил проверить цикл for и while, потому что знал, что время выполнения должно быть точно/примерно одинаковым. Мне было любопытно, почему существует существенная разница в двух исполнениях. –

1

Использование new Date().getTime() дает время часы стены. Но это действительно не то, что мы, как разработчики, хотим (большую часть времени, до тех пор, пока не будем проводить бенчмаркинг, где используется бенчмаркинг уровня предприятия), потому что время настенных часов выполняется многими процессами на задней панели, поэтому для противодействия тому, что Java предоставляет более сложный API для измерения времени.

Чтобы исключить эффекты другой активности системы, вам необходимо вместо этого измерить приложение «Пользовательское время».

  • «время Пользователь» время, потраченное на работу собственного кода приложения.
  • «Время процессора» - это время пользователя и системное время. Это общее время, потраченное на использование процессора для вашего приложения.

Ниже приведен пример, чтобы продемонстрировать расчет времени процессора и пользователя с использованием API ManagementFactory.getThreadMXBean().

Thread thread = new Thread(){ 
      public void run() { 
       for (int i = 0; i < 100000; ++i) { 
        int result = 0; 
        for (int j = 0; j < i + 1; ++j) { 
         result += j; 
        } 
       } 
       System.out.println("FOR approach: ThreadCpuTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()/1000000000d); 
       System.out.println("FOR approach: UserTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadUserTime()/1000000000d); 
      }; 
     }; 
     thread.start(); 

     Thread thread2 = new Thread(){ 
      public void run() { 
       for (int i = 0; i < 100000; ++i) { 
        int result = 0; 
        int j = 0; 
        while (j < i + 1) { 
         result += j++; 
        } 
       } 
       System.out.println("WHILE approach: ThreadCpuTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()/1000000000d); 
       System.out.println("WHILE approach: UserTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadUserTime()/1000000000d); 
      }; 
     }; 
     thread2.start(); 


Сказав, что я действительно не знаю, почему вы получаете неожиданное поведение, я побежал ваш код, как в Eclipse, и IntelliJ IDE, и я всегда получал для подхода цикла, как это быстрее, чем цикл WHILE ,

Возможно, попробуйте перезапустить Eclipse и запустить меньшее количество фоновых процессов или не запускать его. Eclipse, но запустите тесты из командной строки Java, чтобы вы могли точно узнать результаты.

Как видно из ниже байт кода анализа что в то время, и для захода на посадку петли имеет одинаковые коды байт сгенерированные, что означает, что будет же собрать код и, следовательно, же процессорного времени потребуется для выполнения инструкций.

Но практически, когда мы запускаем вашу среду IDE или другие мудрые, это происходит из-за фоновых процессов, поэтому наблюдаются разные времена. Но в этом конкретном случае - WHILE v/s FOR, более целесообразно выполнять анализ байтового кода и заключить, что подходы WHILE и FOR будут выполняться в одно и то же время.


Байт код для цикла:

{ 
    public Test2(); 
    flags: ACC_PUBLIC 
    Code: 
     stack=1, locals=1, args_size=1 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: return 
     LineNumberTable: 
     line 1: 0 

    public static void main(java.lang.String[]); 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=2, args_size=1 
     0: iconst_0 
     1: istore_1 
     2: iload_1 
     3: bipush  10 
     5: if_icmpge  21 
     8: getstatic  #2     // Field java/lang/System.out:Ljava/io/PrintStream; 
     11: iload_1 
     12: invokevirtual #3     // Method java/io/PrintStream.println:(I)V 
     15: iinc   1, 1 
     18: goto   2 
     21: return 
     LineNumberTable: 
     line 3: 0 
     line 4: 8 
     line 3: 15 
     line 6: 21 
     StackMapTable: number_of_entries = 2 
      frame_type = 252 /* append */ 
      offset_delta = 2 
     locals = [ int ] 
      frame_type = 250 /* chop */ 
      offset_delta = 18 

} 

Байт код цикла WHILE:

{ 
    public Test(); 
    flags: ACC_PUBLIC 
    Code: 
     stack=1, locals=1, args_size=1 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: return 
     LineNumberTable: 
     line 1: 0 

    public static void main(java.lang.String[]); 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=2, args_size=1 
     0: iconst_0 
     1: istore_1 
     2: iload_1 
     3: bipush  10 
     5: if_icmpge  21 
     8: getstatic  #2     // Field java/lang/System.out:Ljava/io/PrintStream; 
     11: iload_1 
     12: invokevirtual #3     // Method java/io/PrintStream.println:(I)V 
     15: iinc   1, 1 
     18: goto   2 
     21: return 
     LineNumberTable: 
     line 3: 0 
     line 4: 2 
     line 5: 8 
     line 6: 15 
     line 8: 21 
     StackMapTable: number_of_entries = 2 
      frame_type = 252 /* append */ 
      offset_delta = 2 
     locals = [ int ] 
      frame_type = 18 /* same */ 

} 

Дальнейшее чтение:

+0

"* Теоретически можно утверждать, что оба подхода должны давать одинаковый результат, но практически это не *. => Я очень сомневаюсь, что они разные ... – assylias

+0

@assylias Я побежал результаты, и они оказались разными раз. Даже ОП получает разные результаты. – hagrawal

+0

Как вы измерили? – assylias

Смежные вопросы