2016-05-16 16 views
5

rozważyć następujące kwestie:Czy jest to pomocne optymalizacji pętli w java?

1.

for (final Bar a : bars) { 
     for (final Foo f : foos) { 
      doSomethingWith(f.foo(), a.bar()); 
     } 
    } 

i:

2.

for (final Bar a : bars) { 
     final Object bar = a.bar(); 
     for (final Foo f : foos) { 
      doSomethingWith(f.foo(), bar); 
     } 
    } 

Czy ten rodzaj optymalizacji naprawdę pomocny albo kompilator to zrobić automatycznie, tak?

Twoja odpowiedź zmieni się, jeśli bar() byłby geterem? (np. getBar())

Czy zmieni się odpowiedź, jeśli będę kierować reklamy na system Android?

+2

Praca dla studenta: skompilować obie wersje i porównać wyjście kodu bajtowego. Oto referencja; https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html – DwB

+0

Nie sądzę, że to pytanie jest na poziomie ucznia, ale cóż, zastanawiałem się, czy ktoś już zna odpowiedź na to raczej niż mówienie mi, że można spojrzeć na wyjściowy kod bajtowy, o którym wiem, że mogłem. a także, musiałbym spojrzeć na wyjścia także dla Androida. może to być piekło projektu, aby samodzielnie odpowiedzieć na to pytanie. –

+1

Czy kompilator wie, że 'a.bar()' zawsze zwraca tę samą wartość dla danego 'a'? –

Odpowiedz

2

Próbowałem dwóch przykładów, jak robisz dla swojego pytania. Na tej podstawie muszę powiedzieć, że drugie podejście byłoby lepsze. (Choć nie jestem rozważa wielowątkowości)

Test.java

public class Test{ 

    public static void main(String... args){ 

     String[][] arr2 = new String[5][5]; 
     for (final String[] obj : arr2) 
     { 
      for (final String str : obj) 
      System.out.println(str.length() +" " + obj.length); 
     } 
    } 
} 

po kompilacji, a następnie ponownie decompiling mam to.

* Decompiled with CFR 0_114. 
*/ 
import java.io.PrintStream; 

public class Test { 
    public static /* varargs */ void main(String ... arrstring) { 
     String[][] arrstring2; 
     String[][] arrstring3 = arrstring2 = new String[5][5]; 
     int n = arrstring3.length; 
     for (int i = 0; i < n; ++i) { 
      String[] arrstring4; 
      for (String string : arrstring4 = arrstring3[i]) { //assignment will take place m*n. 
       System.out.println("" + string.length() + " " + arrstring4.length); 
      //this arrstring4.length will execute m*n (in this case).So, this will less efficient than others. 
      } 
     } 
    } 
} 

Test1.java

public class Test1{ 

    public static void main(String... args){ 

     String[][] arr2 = new String[5][5]; 
     for (final String[] obj : arr2) 
     { 
      int value = obj.length; 
      for (final String str : obj) 
       System.out.println(str.length() +" " + value); 
     } 
    } 
} 

po kompilacji, a następnie ponownie decompiling mam to.

/* 
* Decompiled with CFR 0_114. 
*/ 
import java.io.PrintStream; 

public class Test1 { 
    public static /* varargs */ void main(String ... arrstring) { 
     String[][] arrstring2; 
     for (String[] arrstring3 : arrstring2 = new String[5][5]) { 
      int n = arrstring3.length; //Assignment will take place M times only. 
      //this will calculate M times only. So, it will definitely faster than above. 
      for (String string : arrstring3) { 
       System.out.println("" + string.length() + " " + n); 
       //here n is calculate M times but can be printed M*N times. 
      } 
     } 
    } 
} 
-2

Po obejrzeniu komentarze i mające odpowiedzieć na nich, zdałem sobie sprawę, że nawet jeśli kompilator będzie nurkować w zrozumieniu a.bar() „s kodu, byłoby niemożliwe Gwarantujemy że a.bar() wynik nie ulegnie zmianie w wielowątkowy Środowisko takie jak to jest w Javie, także w połączeniu z Reflection jeśli bar jest polem członkowskim, które może zmienić poprzez odbicia i kompilator nie wiedziałby, że z góry, który może spowodować, że prosty return bar; jest nieprzewidywalny.

W tej chwili wydaje mi się racjonalne, aby nadal nurtować tę optymalizację w moim kodzie.

+0

"Gwarantuję, że wynik' a.bar() 'nie zmieniłby się w środowisku wielowątkowym" - chyba że metoda zawiera jakąś synchronizację lub znajomych (jak 'volatile'), kompilator może zignorować inne wątki. Bez synchronizacji JMM gwarantuje brak widoczności zmian dokonanych przez inne wątki i ani HW nie gwarantuje niczego (myśl na pamięć podręczną rdzenia). +++ To powiedziawszy, może to być przydatna optymalizacja na Androida, która nie jest tak inteligentna jak JVM klasy serwera. – maaartinus

+0

Należy zauważyć, że zmiana elementu może nastąpić między iteracjami na obiektach foos, volatile nie pomoże w tym przypadku, a synchronizacja może pomóc tylko wtedy, gdy zostanie to zrobione. trudno mi uwierzyć, że kompilator może analizować poprawność synchronizacji we wszystkich przypadkach ... –

+0

Co jeśli 'a.bar()' jest metodą "finalną", która zwraca pole 'final'? – shmosel

0

zaskakujące, gdyż może ona wydaje spróbować:

for (final Bar a : bars) { 
    innerLoop(a, foos); 
} 

private final innerLoop(Bar a, Collection<Foo> foos) { 
    for (final Foo f : foos) { 
     doSomethingWith(f.foo(), a.bar()); 
} 

tylko mały uszczypnąć, dać mu spróbować

+0

Co masz nadzieję na tym zyskać? Btw. masz również błąd składni. – Hulk