2013-04-25 21 views
10

Java için iç içe geçmiş, süre ve do-while döngülerinin verimliliğini karşılaştırıyorum ve anlamanıza yardımcı olması gereken bazı garip sonuçlara rastlarım.Java döngü verimliliği

public class Loops { 
    public static void main(String[] args) { 
     int L = 100000; // number of iterations per loop 
     // for loop 
     double start = System.currentTimeMillis(); 
     long s1 = 0; 
     for (int i=0; i < L; i++) { 
      for (int j = 0; j < L; j++) { 
       s1 += 1; 
      } 
     } 
     double end = System.currentTimeMillis(); 
     String result1 = String.format("for loop: %.5f", (end-start)/1000); 
     System.out.println(s1); 
     System.out.println(result1); 

     // do-while loop 
     double start1 = System.currentTimeMillis(); 
     int i = 0; 
     long s2 = 0; 
     do { 
      i++; 
      int j = 0; 
      do { 
       s2 += 1; 
       j++; 
      } while (j < L); 
     } while (i < L); 
     double end1 = System.currentTimeMillis(); 
     String result2 = String.format("do-while: %.5f", (end1-start1)/1000); 
     System.out.println(s2); 
     System.out.println(result2); 

     // while loop 
     double start2 = System.currentTimeMillis(); 
     i = 0; 
     long s3 = 0; 
     while (i < L) { 
      i++; 
      int j = 0; 
      while (j < L) { 
       s3 += 1; 
       j++; 
      } 
     } 
     double end2 = System.currentTimeMillis(); 
     String result3 = String.format("while: %.5f", (end2-start2)/1000); 
     System.out.println(s3); 
     System.out.println(result3); 
    } 
} 

Döngülerin tümü ilgili sayaçların toplamı 10 milyara kadardır; Sonuçlar beni perplex:

döngü

: 6,48300

do-while: 0,41200

süre: 9,71500

neden do-while çok çabuk? Bu performans aralığı, L'deki herhangi bir değişikliğe paralel olarak ölçeklenir. Bu döngüleri bağımsız olarak çalıştırıyorum ve aynı şeyi yapıyorlar.

+1

Numaralarınızı çoğalamıyorum. Her ikisi de döngüler için biraz daha yavaş döngü için benimle aynı hızı çalıştırın. – Mysticial

+5

Her durumda, derleyici veya JIT iç döngüyü tamamen kaldırabileceğinden, bu özellikle büyük bir kıyaslama değildir. – Mysticial

+1

Durum böyle olmalı - sadece do-while döngüsü için çalıştırılan bir çeşit optimizasyon. Yine de, bu mekanizma hakkında daha çok şey öğrenmek isterim. – JohnF

cevap

18

Sağladığınız kodu çalıştırdım ve performanstaki bu farklılıkları görmek beni şaşırttı. Merak ettim, araştırmaya başladım ve aslında bu döngülere rağmen aynı şeyleri yapıyor gibi göründüğümde aralarında bazı önemli farklılıklar olduğunu öğrendim.

for loop: 1.43100 
do-while: 0.51300 
while: 1.54500 

Ama daha sonra bu üç döngüler en az 10 kez çalıştırdığınızda bu döngünün her birinin performansı hemen hemen aynıydı: Bu döngü ilk çalıştırdıktan sonra

Benim sonuçları idi.

for loop: 0.43200 
do-while: 0.46100 
while: 0.42900 

tam zamanında, zaman içinde bu döngüler optimize edebilir, ancak farklı bir başlangıç ​​performans için bu döngüler neden bazı farklılık olmalıdır. Aslında aslında iki farklılıklar vardır:

do-while döngü for ve while döngüler Kolaylık olması açısından

daha az karşılaştırmaları yapıyor

  • L = 1

    long s1 = 0; 
    for (int i=0; i < L; i++) { 
        for (int j = 0; j < L; j++) { 
         s1 += 1; 
    

    dış döngü varsayalım : 0 iç döngü: 0 İç döngü: 1 dış döngü: toplam

    int i = 0; 
    long s2 = 0; 
    do { 
        i++; 
        int j = 0; 
        do { 
         s2 += 1; 
         j++; 
        } while (j < L); 
    } while (i < L); 
    

    iç döngüde 1

    4 karşılaştırma: 1 dış döngü: 1

    2 karşılaştırmaların toplamı:

    • Farklı oluşturulan baytkodlarına

    fazla araştırma amacıyla bunu çalışma şeklini etkileyen değil, hafifçe sınıf değişti.

    public class Loops { 
        final static int L = 100000; // number of iterations per loop 
    
        public static void main(String[] args) { 
         int round = 10; 
         while (round-- > 0) { 
          forLoop(); 
          doWhileLoop(); 
          whileLoop(); 
         } 
        } 
    
        private static long whileLoop() { 
         int i = 0; 
         long s3 = 0; 
         while (i++ < L) { 
          int j = 0; 
          while (j++ < L) { 
           s3 += 1; 
          } 
         } 
         return s3; 
        } 
    
        private static long doWhileLoop() { 
         int i = 0; 
         long s2 = 0; 
         do { 
          int j = 0; 
          do { 
           s2 += 1; 
          } while (++j < L); 
         } while (++i < L); 
         return s2; 
        } 
    
        private static long forLoop() { 
         long s1 = 0; 
         for (int i = 0; i < L; i++) { 
          for (int j = 0; j < L; j++) { 
           s1 += 1; 
          } 
         } 
         return s1; 
        } 
    } 
    

    Sonra derlenmiş ve bayt kodu almak için javap -c -s -private -l Loop çağrılan.

    İlk olarak doWhileLoop'un bayt kodu. Şimdi

    0: iconst_0  // push the int value 0 onto the stack 
        1: istore_1  // store int value into variable 1 (i) 
        2: lconst_0  // push the long 0 onto the stack 
        3: lstore_2  // store a long value in a local variable 2 (s2) 
        4: iconst_0  // push the int value 0 onto the stack 
        5: istore 4 // store int value into variable 4 (j) 
        7: lload_2  // load a long value from a local variable 2 (i) 
        8: lconst_1  // push the long 1 onto the stack 
        9: ladd  // add two longs 
        10: lstore_2  // store a long value in a local variable 2 (i) 
        11: iinc 4, 1 // increment local variable 4 (j) by signed byte 1 
        14: iload 4 // load an int value from a local variable 4 (j) 
        16: iload_0  // load an int value from a local variable 0 (L) 
        17: if_icmplt 7 // if value1 is less than value2, branch to instruction at 7 
        20: iinc 1, 1 // increment local variable 1 (i) by signed byte 1 
        23: iload_1  // load an int value from a local variable 1 (i) 
        24: iload_0  // load an int value from a local variable 0 (L) 
        25: if_icmplt 4 // if value1 is less than value2, branch to instruction at 4 
        28: lload_2  // load a long value from a local variable 2 (s2) 
        29: lreturn  // return a long value 
    

    while döngüsü içinde baytkodu:

    0: iconst_0  // push int value 0 onto the stack 
        1: istore_1  // store int value into variable 1 (i) 
        2: lconst_0  // push the long 0 onto the stack 
        3: lstore_2  // store a long value in a local variable 2 (s3) 
        4: goto  26 
        7: iconst_0  // push the int value 0 onto the stack 
        8: istore 4 // store int value into variable 4 (j) 
        10: goto  17 
        13: lload_2  // load a long value from a local variable 2 (s3) 
        14: lconst_1  // push the long 1 onto the stack 
        15: ladd  // add two longs 
        16: lstore_2  // store a long value in a local variable 2 (s3) 
        17: iload 4 // load an int value from a local variable 4 (j) 
        19: iinc 4, 1 // increment local variable 4 (j) by signed byte 1 
        22: iload_0  // load an int value from a local variable 0 (L) 
        23: if_icmplt 13 // if value1 is less than value2, branch to instruction at 13 
        26: iload_1  // load an int value from a local variable 1 (i) 
        27: iinc 1, 1 // increment local variable 1 by signed byte 1 
        30: iload_0  // load an int value from a local variable 0 (L) 
        31: if_icmplt 7 // if value1 is less than value2, branch to instruction at 7 
        34: lload_2  // load a long value from a local variable 2 (s3) 
        35: lreturn  // return a long value 
    

    Her talimat ‪Java bytecode instruction listings dayalı gelmez anlatan yorumlarınızı ekleyebilirsiniz gelmiş çıkış daha okunabilir hale getirmek için.

    Daha yakından incelerseniz, bu iki bayt kodu arasında önemli bir fark olduğunu göreceksiniz. while döngüsü (aynı for döngü için de geçerlidir) bayt kodu sonunda tanımlanmış if ifadeleri (if_icmplt talimatı) içerir. Bunun anlamı, birinci ilmiğin çıkış koşulunu kontrol etmek için bir satırın (26) satırın (26) çağrılması ve benzer şekilde ikinci döngü için bir satırın (17) olmasıdır.

    yukarıdaki baytkodu Mac OS X'te Javac 1.6.0_45 kullanılarak oluşturulmuştur

    Özeti bence

    karşılaştırmalar farklı miktarda artı süre ve döngü bayt için git talimatlarının varlığı Bu döngüler arasındaki performans farkından sorumludur.