Która pętla ma lepszą wydajność? Czemu?

głosy
50
String s = ;
for(i=0;i<....){
    s = some Assignment;
}

lub

for(i=0;i<..){
    String s = some Assignment;
}

I nie trzeba się nigdy używać „s” na zewnątrz pętli. Pierwsza opcja jest chyba lepsze od nowa String nie jest inicjowany za każdym razem. Drugi skutkowałby w zakresie istoty zmiennego ogranicza się do samej pętli.

EDIT: W odpowiedzi na odpowiedź Milhous użytkownika. To byłoby bezcelowe przypisać ciąg na stałe wewnątrz pętli prawda? No, oto niektóre Przypisanie oznacza wartość zmienia dostał z listy poprzez ich powtórzyć.

Ponadto, nie chodzi o to, ponieważ jestem zaniepokojony zarządzania pamięcią. Po prostu chcę wiedzieć, co jest lepsze.

Utwórz 21/09/2008 o 03:46
źródło użytkownik
W innych językach...                            


8 odpowiedzi

głosy
107

Ograniczony zakres jest najlepszy

Użyj drugą opcję:

for ( ... ) {
  String s = ...;
}

Zakres nie wpływa na wydajność

Jeśli kod skompilowany od siebie (z JDK za zdemontować javapnarzędzie), widać, że pętla kompiluje się dokładnie tych samych instrukcji JVM w obu przypadkach. Należy również zauważyć, że Brian R. Bondy za „Opcja nr 3” jest identyczna opcję nr 1. Nic dodatkowo dodaje lub usuwa ze stosu przy użyciu ściślejszej zakres, a same dane są używane na stosie w obu przypadkach.

Uniknąć przedwczesnego Inicjowanie

Jedyna różnica pomiędzy tymi dwoma przypadkami jest to, że w pierwszym przypadku, zmienna sniepotrzebnie zainicjowana. Jest to osobny problem z lokalizacji deklaracji zmiennej. Dodaje dwa zmarnowane instrukcje (załadować ciąg stały i przechowywać go w szczelinie ramki stosu). Dobrym narzędziem do analizy statycznej będzie cię ostrzec, że nigdy nie czyta wartość można przypisać do si dobry kompilator JIT będzie prawdopodobnie opuszczać go w czasie wykonywania.

Można to naprawić po prostu za pomocą pustą deklarację (tj String s;), ale jest to uważane za złą praktyką i ma inny efekt uboczny omówione poniżej.

Często wartość podrobiony lubię nulljest przypisana do zmiennej po prostu uciszyć błąd kompilatora, że zmienna jest odczytywana bez zainicjowany. Ten błąd może być traktowane jako wskazówka, że zakres zmiennej jest zbyt duża, i że jest zadeklarowane przed to jest potrzebne, aby otrzymać poprawną wartość. Puste deklaracje zmuszają rozważyć każdą ścieżkę kodu; nie ignoruj tego cennego ostrzeżenie przez przypisanie wartości fałszywe.

Oszczędzać stos Slots

Jak już wspomniano, podczas gdy JVM instrukcje są takie same w obu przypadkach, istnieje subtelny efekt uboczny, który czyni go najlepiej na poziomie JVM, aby korzystać z najbardziej ograniczony zakres możliwych. Jest to widoczne w „zmiennej lokalnej tabeli” dla metody. Zastanów się, co się dzieje, gdy masz wiele pętli, przy zmiennych zadeklarowanych w niepotrzebnie dużym zakresie:

void x(String[] strings, Integer[] integers) {
  String s;
  for (int i = 0; i < strings.length; ++i) {
    s = strings[0];
    ...
  }
  Integer n;
  for (int i = 0; i < integers.length; ++i) {
    n = integers[i];
    ...
  }
}

Zmienne si nmogą być deklarowane wewnątrz swoich pętli, ale ponieważ nie są one, kompilator używa dwóch „szczeliny” w ramce stosu. Jeśli zostały zgłoszone w pętli, kompilator może ponownie ten sam otwór, przez co ramki stosu mniejsze.

Co tak naprawde sie liczy

Jednak większość z tych problemów są nieistotne. Dobrym kompilator JIT będzie widać, że nie jest możliwe, aby odczytać wartość początkową jesteś rozrzutnie przypisaniem i optymalizacji zadanie z dala. Zapisywanie gniazdo tu czy tam nie będzie złamać lub aplikacji.

Ważną rzeczą jest, aby Twój kod czytelny i łatwy do utrzymania, a w związku z tym, przy użyciu ograniczony zakres jest wyraźnie lepiej. Im mniejszy zakres zmiennej, tym łatwiej jest zrozumieć, w jaki sposób jest on używany i jaki wpływ wszelkie zmiany kodu będzie mieć.

Odpowiedział 21/09/2008 o 07:24
źródło użytkownik

głosy
22

W teorii , że to marnotrawstwo zasobów zadeklarować ciąg wewnątrz pętli. W praktyce jednak oba fragmenty ty przedstawionych zostanie skompilowany w dół do tego samego kodu (deklarację poza pętlą).

Dlatego, jeśli kompilator robi dowolną ilość optymalizacji, nie ma różnicy.

Odpowiedział 21/09/2008 o 03:48
źródło użytkownik

głosy
17

Na ogół wybrałbym tę drugą, ponieważ zakres zmiennej „s” jest ograniczony do pętli. Korzyści:

  • To jest lepsze dla programisty, ponieważ nie trzeba się martwić o „s” wykorzystywane są ponownie gdzieś później w funkcji
  • To jest lepsze dla kompilatora, ponieważ zakres zmiennej jest mniejsza, a więc może to potencjalnie wyrządzić więcej analizę i optymalizację
  • To jest lepsze dla przyszłych czytelników, ponieważ nie będą się zastanawiać, dlaczego zmiennej „s” jest zadeklarowana poza pętlą, jeśli nigdy nie używane później
Odpowiedział 21/09/2008 o 03:51
źródło użytkownik

głosy
6

Jeśli chcesz przyspieszyć pętli, wolę deklarowania zmiennej max obok licznika tak, że nie są potrzebne żadne powtarzające wyszukiwań dla condidtion:

zamiast

for (int i = 0; i < array.length; i++) {
  Object next = array[i];
}

wolę

for (int i = 0, max = array.lenth; i < max; i++) {
  Object next = array[i];
}

Wszelkie inne rzeczy, które powinny być brane pod uwagę już wspomniano, więc po prostu moje dwa centy (patrz Ericksons post)

Powitać, Ghad

Odpowiedział 21/09/2008 o 14:55
źródło użytkownik

głosy
4

Aby dodać na trochę do @ odpowiedź Esteban Araya , będą one obie wymagają utworzenia nowego łańcucha przy każdym przejściu pętli (jako wartości zwracanej przez some Assignmentwyrażenie). Struny te muszą być zbierane śmieci w obu kierunkach.

Odpowiedział 21/09/2008 o 03:54
źródło użytkownik

głosy
3

Wiem, że jest to stara sprawa, ale pomyślałem, że dodam trochę, że jest lekko związanej.

Zauważyłem podczas przeglądania kodu źródłowego Javy, że niektóre metody, takie jak String.contentEquals (duplikaty poniżej) sprawia, że ​​zbędne zmienne lokalne, które są po prostu kopie zmiennych klasowych. Wierzę, że istnieje gdzieś komentarz, który sugerował, że dostęp do zmiennych lokalnych jest szybszy niż dostęp do zmiennych klasy.

W tym przypadku „v1” i „v2” są pozornie niepotrzebne i mogą być wyeliminowane w celu uproszczenia kodu, ale zostały dodane w celu zwiększenia wydajności.

public boolean contentEquals(StringBuffer sb) {
    synchronized(sb) {
        if (count != sb.length())
            return false;
        char v1[] = value;
        char v2[] = sb.getValue();
        int i = offset;
        int j = 0;
        int n = count;
        while (n-- != 0) {
            if (v1[i++] != v2[j++])
                return false;
        }
    }
    return true;
}
Odpowiedział 13/04/2009 o 21:13
źródło użytkownik

głosy
1

Kiedy używam wielu wątków (50+) Potem dowiedziałem się, że jest to bardzo skuteczny sposób postępowania w sprawach z wątku duch nie jest w stanie zamknąć proces prawidłowo .... jeśli się mylę, proszę dać mi znać, dlaczego Nie mam racji:

Process one;
BufferedInputStream two;
try{
one = Runtime.getRuntime().exec(command);
two = new BufferedInputStream(one.getInputStream());
}
}catch(e){
e.printstacktrace
}
finally{
//null to ensure they are erased
one = null;
two = null;
//nudge the gc
System.gc();
}
Odpowiedział 29/07/2013 o 22:10
źródło użytkownik

głosy
1

Wydaje mi się, że potrzebujemy więcej specyfikację problemu.

s = some Assignment;

nie jest określony jako do jakiego rodzaju przypisania tego jest. Jeśli zadanie jest

s = "" + i + "";

Następnie nowy żądło musi zostać przydzielone.

ale jeśli jest to

s = some Constant;

s będzie jedynie wskazywać na stałe miejsce w pamięci, a więc pierwsza wersja będzie bardziej wydajna pamięć.

Wydaje mi trochę głupio się martwić za dużo optymalizacji pętli for dla interpretowanym lang IMHO.

Odpowiedział 21/09/2008 o 06:32
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more