2014-11-24 37 views
5

Neden Stream api ile aynı dizide birden çok kez yineleme yapmanın böyle bir performansla sonuçlandığını anlayamıyorum!Java8 Stream performansı ile şaşırtma

aşağıdaki paragrafa bakın. Emin JVM için

public class WhyIsDifferent { 

public static void main(String[] args) { 

    int[] values = getArray(); 
    Iterate(values, 598, 600); // 70 ms 
    Iterate(values, 200, 202); // 0 ms 
    Iterate(values, 700, 702); // 0 ms 
    Iterate(values, 300, 310); // 1 ms 
} 

public static void Iterate(int[] values, int from, int to) { 
    long start = System.currentTimeMillis(); 
    IntStream.of(values).filter(i -> i < to && i > from) 
    .forEach(i -> 
     System.out.println(i) // do a something 
    ); 
    System.out.println("Time:" + (System.currentTimeMillis() - start)); 
} 

public static int[] getArray() { 
    int[] values = new int[1000]; 
    for (int i = 0; i < 1000; i++) { 
     values[i] = i; 
    } 
    return values; 
} 
} 

kodu optimize ama bu oluyor bilmiyorum ?? Bu harika! Bunun neden yaşandığı hakkında bir fikrin var mı?

-

Ben Ubuntu 14.04// Oracle jdk/intel cpu üzerinde test ediyorum.

+2

Bu, işini yapan [JIT] (http://en.wikipedia.org/wiki/Just-in-time_compilation) arkadaşımdır. –

+0

Ne @LuiggiMendoza diyor ki; ve daha önceleri "tatlı noktaya" bastığınız için, varsayılan olarak Oracle JVM, aynı kod bloğunun 1000 çalıştırılmasından sonra eniyileyecektir. – fge

+0

Biliyorum ama nasıl? JIT filtre parçasını nasıl belirler? Bu garip! –

cevap

9

JIT derleyicisi değil. Bu 70 milisaniyenin çoğu tüm lambda alt sisteminin başlatılması için harcanır (bu mantığın giriş noktası muhtemelen LambdaMetaFactory sınıfıdır) ve iyi bir bit aynı zamanda lambda önyükleme çağrısı (bağlantı fazında belirtildiği şekilde harcanır. kullanıcı fge). senin gibi ama ayrı ayrı ölçülen tüm adımlarla, aynı bu yöntemi göz atın (ve nanoTime kullanın):

Predicate lambda creation time:53 
Action lambda creation time:2 
Stream creation time:2 
599 
Stream consumption time:1 
Predicate lambda creation time:0 
Action lambda creation time:0 
Stream creation time:0 
201 
... all timings zero from here on... 

Bunu görebilirsiniz:

public static void Iterate(int[] values, int from, int to) { 
    long start = System.nanoTime(); 
    final IntPredicate predicate = i -> i < to && i > from; 
    System.out.println("Predicate lambda creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start)); 
    start = System.nanoTime(); 
    final IntConsumer action = System.out::println; 
    System.out.println("Action lambda creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start)); 
    start = System.nanoTime(); 
    final IntStream stream = IntStream.of(values).filter(predicate); 
    System.out.println("Stream creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start)); 
    start = System.nanoTime(); 
    stream.forEach(action); 
    System.out.println("Stream consumption time:" + NANOSECONDS.toMillis(System.nanoTime() - start)); 
} 

Bu benim makinede basıldığı budur ilk aramanın tüm yükü, lambda oluşturma bölümünde (ilk çalıştırmada, genel başlatma ve bağlantıyı içerir) ve akış oluşturmanın da biraz zaman almasıdır. Gerçek akış tüketimi, tüm durumlarda sıfır zaman alır.

Bu etki, HotSpot'un geçerli sürümü ile aklınızda tutmanız gereken bir şeydir: lambda bootstrap pahalı bir şeydir.

Son Not: Eğer lambda oluşturma ifadeleri yeniden düzenlemek durumunda, zaman çoğunluğu oluşturulacak ilk lambda kalır olduğunu göreceksiniz. Bu da bize aslında başlangıç ​​maliyetinin çoğunu taşıyan bir lambda'nın sadece ilk genel yaratımı olduğunu gösteriyor.

+0

Farklı dizilerle yöntemi çağırmak bile aynı performansa neden olur? Lambda bootstrap nedir? hakkında çalışmak için herhangi bir referans var mı? –

+1

Yeni düzenlememe göz atın, tüm oluşturma adımlarını (iki lambda ve akış) ayırdım. Şimdi akım tüketimi ('forEach' çağrısı) ilk seferde bile zaman almaz. Dizinin kendisi bu konuyla ilgisizdir --- siz yapmadığınız sürece * gerçekten * büyük. –

+1

Materyal hakkında, ['LambdaMetaFactory'] javadoc'unu (https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html) harika bir başlangıç ​​noktası olarak öneririm. –