Dlaczego Kompilatory są takie głupie?

Zawsze zastanawiam się, dlaczego Kompilatory nie potrafią wymyślić prostych rzeczy, które są oczywiste dla ludzkiego oka. Robią wiele prostych optymalizacji, ale nigdy nie coś nawet trochę skomplikowanego. Na przykład ten kod zajmuje około 6 sekund na moim komputerze, aby wydrukować wartość zero (używając java 1.6):

int x = 0;
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    x += x + x + x + x + x;
}
System.out.println(x);

Jest całkowicie oczywiste, że x nigdy się nie zmienia, więc bez względu na to, jak często dodajesz 0 do siebie, pozostaje zero. Więc kompilator teoretycznie mógłby zastąpić to System.Wynocha.println(0)

Albo nawet lepiej, to trwa 23 sekundy:
public int slow() {
    String s = "x";
    for (int i = 0; i < 100000; ++i) {
        s += "x";
    }
    return 10;
}

Najpierw kompilator mógł zauważyć, że w rzeczywistości tworzę ciąg s o wartości 100000 "x", więc mógł automatycznie użyć zamiast niego S StringBuilder, lub nawet lepiej bezpośrednio zastąpić go wynikowym ciągiem, ponieważ jest zawsze taki sam. Po drugie, nie rozpoznaje, że w ogóle nie używam łańcucha, więc cała pętla może zostać odrzucona!

Dlaczego, po tak dużej sile roboczej idzie w szybkim Kompilatory, czy nadal są tak względnie głupie?

EDIT: oczywiście są to głupie przykłady, których nigdzie nie należy używać. Ale ilekroć muszę przepisać piękny i bardzo czytelny kod na coś nieczytelnego, aby kompilator był szczęśliwy i generował szybki kod, zastanawiam się, dlaczego Kompilatory lub inne zautomatyzowane narzędzia nie mogą wykonać tej pracy dla mnie.

Author: martinus, 2009-01-02

28 answers

Nie wiem. Czasami Kompilatory są całkiem sprytne. Rozważ następujący program C:

#include <stdio.h>  /* printf() */

int factorial(int n) {
   return n == 0 ? 1 : n * factorial(n - 1);
}

int main() {
   int n = 10;

   printf("factorial(%d) = %d\n", n, factorial(n));

   return 0;
}

W mojej wersji GCC (4.3.2 na Debian testing), skompilowany bez optimizaitons lub-O1, generuje kod dla factorial (), jak można się spodziewać, używając rekurencyjnego wywołania do obliczenia wartości. Ale na -O2 robi coś ciekawego: kompiluje się do ciasnej pętli:

    factorial:
   .LFB13:
           testl   %edi, %edi
           movl    $1, %eax
           je  .L3
           .p2align 4,,10
           .p2align 3
   .L4:
           imull   %edi, %eax
           subl    $1, %edi
           jne .L4
   .L3:
           rep
           ret
Imponujące. Wywołanie rekurencyjne (nawet nie rekurencyjne) zostało całkowicie wyeliminowane, więc obecnie factorial używa przestrzeni stosu O(1) zamiast O (N). I chociaż mam tylko bardzo powierzchowną wiedzę na temat montażu x86 (właściwie AMD64 w tym przypadku, ale nie sądzę, aby któryś z rozszerzeń AMD64 był używany powyżej), wątpię, aby można było napisać lepszą wersję ręcznie. Ale to, co naprawdę rozwaliło mój umysł, to kod, który wygenerował na-O3. Realizacja faktorii pozostała bez zmian. Ale main () zmienił:
    main:
   .LFB14:
           subq    $8, %rsp
   .LCFI0:
           movl    $3628800, %edx
           movl    $10, %esi
           movl    $.LC0, %edi
           xorl    %eax, %eax
           call    printf
           xorl    %eax, %eax
           addq    $8, %rsp
           ret

Widzisz linię movl $3628800, %edx? gcc jest Pre-computing factorial (10) w czasie kompilacji. nawet nie wywołuje funkcji factorial (). Niesamowite. Mój kapelusz jest off do zespołu rozwoju GCC.

Oczywiście wszystkie zwykłe zastrzeżenia mają zastosowanie, jest to tylko przykład zabawki, przedwczesna optymalizacja jest źródłem wszelkiego zła, itd, itp, ale ilustruje to, że Kompilatory są często mądrzejsze niż myślisz. Jeśli uważasz, że możesz zrobić lepszą robotę ręcznie, prawie na pewno się mylisz.

(zaadaptowane z posta na moim blogu .)

 105
Author: Jason Creighton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-12 19:04:32

Moim zdaniem, nie sądzę, aby zadaniem kompilatora było naprawianie tego, co jest, szczerze mówiąc, złym kodowaniem. Dość wyraźnie powiedziałeś kompilatorowi, że chcesz, aby pierwsza pętla została wykonana. To samo co:

x = 0
sleep 6 // Let's assume this is defined somewhere.
print x

Nie chciałbym, aby kompilator usunął Moje sleep stwierdzenie tylko dlatego, że nic nie zrobił. Możesz argumentować, że oświadczenie dotyczące snu jest wyraźną prośbą o opóźnienie, podczas gdy twój przykład nie jest. Ale wtedy pozwolicie kompilatorowi na podejmowanie bardzo wysokich decyzji o tym, co Twój kod powinien wystarczyć, i uważam, że jest to zła rzecz.

Kod i kompilator, który go przetwarza, są narzędziami i musisz być narzędziowcem, jeśli chcesz efektywnie z nich korzystać. Ile pił łańcuchowych 12 " odmówi ścięcia 30-calowego drzewa? Ile wierteł automatycznie przełączy się w tryb młota, jeśli wykryją betonową ścianę?

Żadnych, podejrzewam, a to dlatego, że koszt zaprojektowania tego do produktu byłby horrendalny na początek. Ale, co ważniejsze, ty nie powinieneś używać wierteł ani pił łańcuchowych, jeśli nie wiesz, co robisz. Na przykład: jeśli nie wiesz, co to jest kickback (bardzo łatwy sposób dla nowicjusza, aby zdjąć rękę), trzymaj się z dala od pił łańcuchowych, dopóki tego nie zrobisz.

Jestem za pozwoleniem kompilatorom sugerować ulepszenia, ale wolę sam zachować kontrolę. Kompilator nie powinien jednostronnie decydować, że pętla jest niepotrzebna.

Na przykład zrobiłem pętle czasowe w systemach wbudowanych, gdzie Prędkość zegara procesora jest dokładnie znana, ale nie ma niezawodnego urządzenia do pomiaru czasu. W takim przypadku możesz dokładnie obliczyć, jak długo zajmie dana pętla i użyć jej do kontrolowania, jak często coś się dzieje. To by nie zadziałało, gdyby kompilator (lub asembler w tym przypadku) uznał, że moja pętla jest bezużyteczna i zoptymalizował ją.

Mimo tego, pozwolę sobie zostawić wam starą historię kompilatora VAX FORTRAN, który przechodził test porównawczy wydajności i okazało się, że był on[11]} o wiele rzędów wielkości szybszy niż jego najbliższy konkurent.

Okazało się, że kompilator zauważył, że wynik pętli benchmark nie był używany nigdzie indziej i zoptymalizował pętle w zapomnienie.

 111
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-04 02:21:16

Mówienie z punktu widzenia C / C++:

Twój pierwszy przykład zostanie zoptymalizowany przez większość kompilatorów. Jeśli kompilator Javy Z Sun naprawdę wykonuje tę pętlę, to jest to wina kompilatorów, ale uwierzcie mi na słowo, że każdy post 1990 c, c++ lub Fortran-compiler całkowicie eliminuje taką pętlę.

Twój drugi przykład nie może być zoptymalizowany w większości języków, ponieważ alokacja pamięci odbywa się jako efekt uboczny łączenia łańcuchów. Jeśli kompilator zoptymalizowałby kod, wzór alokacji pamięci ulegnie zmianie, co może prowadzić do efektów, których programista stara się uniknąć. Fragmentacja pamięci i związane z tym problemy to problemy, z którymi programiści wbudowani wciąż borykają się każdego dnia.

Ogólnie jestem zadowolony z optymalizacji Kompilatory mogą zrobić te dni.

 46
Author: Nils Pipenbrinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:56:55

Kompilatory są zaprojektowane tak, aby były przewidywalne . To może sprawić, że będą wyglądać głupio od czasu do czasu, ale to jest w porządku. Cele kompilatora to

  • Powinieneś być w stanie spojrzeć na swój kod i dokonać rozsądnych prognoz dotyczących jego wydajności.

  • Niewielkie zmiany w kodzie nie powinny skutkować dramatycznymi różnicami w wydajności.

  • Jeśli mała zmiana wydaje się programiście, że powinna poprawić wydajność, powinna przynajmniej nie obniża wydajności (chyba że w sprzęcie dzieją się zaskakujące rzeczy).

Wszystkie te kryteria zwalczają" magiczne " optymalizacje, które dotyczą tylko przypadków narożnych.


Oba przykłady mają zmienną zaktualizowaną w pętli, ale nie używaną gdzie indziej . Ten przypadek jest w rzeczywistości dość trudny do wykrycia, chyba że używasz jakiegoś frameworka, który może łączyć eliminację martwego kodu z innymi optymalizacjami, takimi jak propagacja kopii lub stała rozmnażanie. Do prostego optymalizatora przepływu danych zmienna nie wygląda na martwą. Aby zrozumieć, dlaczego ten problem jest trudny, zobacz artykuł Lerner, Grove, and Chambers z POPL 2002 {27]}, który wykorzystuje ten właśnie przykład i wyjaśnia, dlaczego jest trudny.

 23
Author: Norman Ramsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-25 05:34:37

Kompilator JIT HotSpot zoptymalizuje tylko kod, który został uruchomiony przez jakiś czas. Do czasu, gdy kod jest gorący, pętla została już uruchomiona i kompilator JIT musi poczekać do następnego razu, gdy metoda zostanie wprowadzona, aby szukać sposobów na optymalizację pętli. Jeśli wywołasz metodę kilka razy, możesz zobaczyć lepszą wydajność.

Jest to omówione w HotSpot FAQ , pod pytaniem " piszę prostą pętlę do czasu prostą operację i jest to powolne. Czym jestem źle robisz?".

 16
Author: Rune,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:40:53

Poważnie? Po co ktoś miałby pisać taki kod? IMHO kod, a nie kompilator to tu" głupi " byt. Jestem całkowicie zadowolony, że pisarze kompilatorów nie tracą czasu, próbując zoptymalizować coś takiego.

Edycja / Wyjaśnienie: Wiem, że kod w pytaniu ma służyć jako przykład, ale to tylko dowodzi mojej racji: albo trzeba próbować, albo być dość nieświadomym, aby napisać w ten sposób wyjątkowo nieefektywny kod. To nie jest zadaniem kompilatora jest trzymanie nas za rękę, żebyśmy nie pisali strasznego kodu. Naszym obowiązkiem, jako osób piszących kod, jest wiedzieć wystarczająco dużo o naszych narzędziach, aby pisać efektywnie i jasno.

 14
Author: Daniel Schaffer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:10:25

Cóż, mogę mówić tylko o C++, ponieważ jestem całkowicie początkującym Javą. W C++ Kompilatory mogą ignorować wszelkie wymagania językowe stawiane przez Standard, o ile zachowanie obserwowalne jest , tak jak-jeśli kompilator faktycznie emuluje wszystkie reguły, które są umieszczane przez Standard. Zachowanie obserwowalne jest definiowane jako wszelkie odczyty i zapisy do zmiennych danych oraz wywołania funkcji bibliotecznych . Rozważ to:

extern int x; // defined elsewhere
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    x += x + x + x + x + x;
}
return x;

Kompilator C++ może zoptymalizuj ten fragment kodu i po prostu dodaj odpowiednią wartość do x, która wynikałaby z tej pętli raz, ponieważ kod zachowuje się tak, jakby pętla nigdy się nie wydarzyła, i nie są zaangażowane żadne zmienne DANE, ani funkcje biblioteczne, które mogłyby powodować potrzebne efekty uboczne. Teraz rozważmy zmienne lotne:

extern volatile int x; // defined elsewhere
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    x += x + x + x + x + x;
}
return x;

Kompilator nie ma prawa do tej samej optymalizacji, ponieważ nie może udowodnić, że efekty uboczne wywołane zapisem do x nie mogą wpływać na obserwowalne zachowanie programu. Wszakże x może być ustawione na komórkę pamięci obserwowaną przez jakieś urządzenie sprzętowe, które wyzwalałoby przy każdym zapisie.


Mówiąc o Javie, przetestowałem Twoją pętlę i zdarza się, że kompilator GNU Java (gcj) zajmuje zbyt dużo czasu, aby zakończyć Twoją pętlę (po prostu nie skończył i ją zabiłem). Włączyłem flagi optymalizacyjne (- O2) i tak się stało, że Wydrukowano 0 od razu:

[js@HOST2 java]$ gcj --main=Optimize -O2 Optimize.java
[js@HOST2 java]$ ./a.out
0
[js@HOST2 java]$

Może ta obserwacja mogłaby być pomocna w tym wątek? Dlaczego dla gcj jest tak szybko? Cóż, jednym z powodów jest na pewno to, że gcj kompiluje się do kodu maszynowego, a więc nie ma możliwości optymalizacji tego kodu w oparciu o zachowanie kodu w trybie runtime. Bierze całą swoją siłę razem i stara się optymalizować jak najwięcej w czasie kompilacji. Jednak maszyna wirtualna może skompilować kod w odpowiednim czasie, jak pokazuje to wyjście Javy dla tego kodu:

class Optimize {
    private static int doIt() {
        int x = 0;
        for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
            x += x + x + x + x + x;
        }
        return x;
    }
    public static void main(String[] args) {
        for(int i=0;i<5;i++) {
            doIt();
        }
    }
}

Wyjście dla java -XX:+PrintCompilation Optimize:

1       java.lang.String::hashCode (60 bytes)
1%      Optimize::doIt @ 4 (30 bytes)
2       Optimize::doIt (30 bytes)

Jak widzimy, JIT kompiluje doIt Funkcja 2 razy. Na podstawie obserwacji pierwszego wykonania kompiluje go po raz drugi. Ale tak się składa, że ma ten sam rozmiar co bajt dwa razy, co sugeruje, że pętla jest nadal na miejscu.

Jak pokazuje inny programista, czas wykonywania pewnych martwych pętli nawet jest zwiększany w niektórych przypadkach dla późniejszego skompilowanego kodu. Zgłosił błąd, który można przeczytać tutaj i jest od 24. Październik 2008.

 12
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-17 12:04:46

W pierwszym przykładzie jest to optymalizacja, która działa tylko wtedy, gdy wartość jest równa zero. Dodatkowe polecenie if w kompilatorze potrzebne do szukania tej rzadko spotykanej klauzuli może po prostu nie być tego warte(ponieważ będzie musiał to sprawdzić na każdej zmiennej). Ponadto, co z tym:

int x = 1;
int y = 1;
int z = x - y;
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    z += z + z + z + z + z;
}
System.out.println(z);

To nadal oczywiście to samo, ale teraz jest dodatkowy przypadek, dla którego musimy kodować w kompilatorze. Jest po prostu nieskończona ilość sposobów, że może skończyć się zerem, że nie są warte kodowania, i myślę, że można powiedzieć, że jeśli masz zamiar mieć jeden z nich można równie dobrze mieć je wszystkie.

Niektóre optymalizacje dbają o drugi przykład, który opublikowałeś, ale myślę, że widziałem go bardziej w językach funkcyjnych, a nie w Javie. Wielką rzeczą, która sprawia, że trudno jest w nowszych językach jestmonkey-patching . Teraz += może mieć efekt uboczny, co oznacza, że jeśli go zoptymalizujemy, to jest potencjalnie źle (np. dodanie Funkcja +=, która wyświetla bieżącą wartość, będzie oznaczać zupełnie inny program).

Ale wszystko sprowadza się do tego samego: jest po prostu zbyt wiele przypadków, których trzeba szukać, aby upewnić się, że nie są wykonywane żadne skutki uboczne, które potencjalnie zmienią stan końcowego programu.

Po prostu łatwiej jest poświęcić dodatkową chwilę i upewnić się, że to, co piszesz, jest tym, co naprawdę chcesz, aby komputer zrobił. :)

 9
Author: Chris Bunch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-05 22:32:33

Kompilatory w ogóle są bardzo inteligentne.

Należy wziąć pod uwagę, że muszą one uwzględniać każdy możliwy wyjątek lub sytuację, w której optymalizacja lub ponowne faktorowanie kodu może spowodować niepożądane skutki uboczne.

Rzeczy takie jak programy wątkowe, aliasing wskaźników, dynamicznie powiązany kod i efekty uboczne (wywołania systemowe/pamięć alloc) itp. sprawiają, że formalnie prooving refaktoring jest bardzo trudny.

Mimo, że twój przykład jest prosty, nadal mogą być trudne sytuacje do zastanów się.

Jeśli chodzi o argument StringBuilder, to nie jest to zadanie kompilatorów, aby wybrać, które struktury danych mają być używane dla Ciebie.

Jeśli chcesz bardziej wydajnych optymalizacji przejdź do silniej typowanego języka, takiego jak fortran lub haskell, gdzie Kompilatory otrzymują znacznie więcej informacji do pracy.

Większość kursów uczących kompilatorów / optymalizacji (nawet acedemically) daje poczucie uznania o tym, w jaki sposób gerneral formalnie prooven optimisatons zamiast hacking konkretne przypadki to bardzo trudny problem.

 7
Author: Akusete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:27:32

Myślę, że nie doceniasz, jak wiele pracy jest, aby upewnić się, że jeden kawałek kodu nie wpływa na inny kawałek kodu. Wystarczy mała zmiana w Twoich przykładach x, i I s mogą wskazywać na tę samą pamięć. Gdy jedna ze zmiennych jest wskaźnikiem, znacznie trudniej jest stwierdzić, jaki kod może mieć skutki uboczne w zależności od tego, co wskazuje.

Myślę również, że ludzie, którzy programują Kompilatory, woleliby poświęcić czas na tworzenie optymalizacji, które nie są tak łatwe dla ludzi.

 6
Author: Chad DeShon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:13:25

Bo jeszcze nie jesteśmy. Równie łatwo można było zapytać: "dlaczego wciąż muszę pisać programy... dlaczego nie mogę po prostu podać w dokumencie wymagań i poprosić komputer o napisanie aplikacji dla mnie?"

Twórcy kompilatorów poświęcają czas na małe rzeczy, ponieważ są to rzeczy, za którymi programiści aplikacji Zwykle tęsknią.

Poza tym, nie mogą zakładać zbyt wiele (może Twoja pętla była jakimś opóźnieniem czasu w getcie lub czymś takim)?

 3
Author: Giovanni Galbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:19:01

To wieczny wyścig zbrojeń pomiędzy twórcami kompilatorów i programistami.

Nieskomplikowane przykłady działają świetnie - większość kompilatorów optymalizuje oczywiście bezużyteczny kod.

Contrived examines will always stump the compiler. Dowód, że każdy programista jest mądrzejszy od każdego programu.

W przyszłości będziesz potrzebował więcej wymyślonych przykładów niż te, które tu zamieściłeś.

 2
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:21:04

Ponieważ inni odpowiednio zajęli się pierwszą częścią twojego pytania, postaram się zająć drugą częścią, tj. "automatically uses StringBuilder instead".

Istnieje kilka dobrych powodów, aby nie robić tego, co sugerujesz, ale największym czynnikiem w praktyce jest prawdopodobnie to, że optymalizator działa długo po tym, jak rzeczywisty kod źródłowy został przetrawiony i zapomniany. Optymalizatory zazwyczaj działają na wygenerowanym kodzie bajtowym (lub złożeniu, kodzie trzech adresów, kodzie maszynowym, itd.), lub na abstrakcyjnych drzewach składni, które wynikają z parsowania kodu. Optymalizatory na ogół nie wiedzą nic o bibliotekach uruchomieniowych (ani o żadnych bibliotekach w ogóle), a zamiast tego działają na poziomie instrukcji (tj. przepływ kontroli niskiego poziomu i alokacja rejestru).

Po drugie, w miarę rozwoju bibliotek (esp. w Javie) znacznie szybciej niż języki, nadążanie za nimi i wiedza, co deprecates co i jaki inny komponent biblioteki może być lepiej dostosowany do zadania byłoby zadaniem herkulesowym. Prawdopodobnie jest to również niemożliwe, ponieważ proponowany optymalizator musiałby dokładnie zrozumieć zarówno twoje intencje, jak i intencje każdego dostępnego komponentu biblioteki i jakoś znaleźć mapowanie między nimi.

Wreszcie, jak inni powiedzieli (myślę), kompilator / optymalizator writer może rozsądnie założyć, że programista piszący kod wejściowy nie jest martwy. Byłoby stratą czasu, aby poświęcić znaczny wysiłek na asinine szczególnych przypadkach, takich jak te, gdy inne, bardziej ogólne optymalizacje obfitują. Ponadto, jak już wspominali inni, pozornie martwy mózg kod może mieć rzeczywisty cel(blokada wirowania, zajęte oczekiwanie przed wydajnością na poziomie systemu itp.), a kompilator musi uszanować to, o co prosi programista (jeśli jest poprawny składniowo i semantycznie).

 2
Author: Drew Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 03:18:04

Czy skompilowałeś do wydania kodu? Myślę, że dobry kompilator wykrywa w drugim przykładzie, że łańcuch nigdy nie jest używany i usuwa całą pętlę.

 1
Author: Rauhotz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:17:28

Właściwie, Java powinna użyć string builder w drugim przykładzie.

Podstawowym problemem przy próbach optymalizacji tych przykładów jest to, że wymagałoby to udowodnienia twierdzenia . Co oznacza, że kompilator musiałby skonstruować matematyczny dowód tego, co twój kod faktycznie zrobi. A to wcale nie jest małe zadanie. W rzeczywistości, możliwość udowodnienia, że cały kod naprawdę działa, jest równoznaczna z problemem wstrzymania.

Jasne, że możesz wymyśl trywialne przykłady, ale liczba trywialnych przykładów jest nieograniczona. Zawsze można wymyślić coś innego, więc nie ma sposobu, aby złapać je wszystkie.

Oczywiście możliwe jest udowodnienie, że jakiś kod nie ma żadnego wpływu, jak w Twoich przykładach. Co chcesz zrobić, to mieć kompilator optymalizacji dala każdy problem, który może być udowodnione nieużywane w czasie P.

Ale w każdym razie, to jest mnóstwo pracy i to nie daje Ci tyle. Ludzie wydają dużo czas próbuje znaleźć sposoby, aby zapobiec programom z błędów w nich, i systemy typu, takie jak te w Javie i Scali są próby zapobiegania błędom, ale teraz nikt nie używa systemów typu do składania oświadczeń o czasie wykonania, o ile wiem.

Może warto przyjrzeć się Haskelowi, który według mnie ma najbardziej zaawansowaną teorię dowodzącą rzeczy, chociaż nie jestem tego pewien. Sam Nie wiem.

 1
Author: Chad Okere,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 02:41:45

Głównie narzekasz na "Dlaczego kompilator Javy jest taki głupi", ponieważ większość kompilatorów innych języków jest znacznie mądrzejsza.

Powód głupoty kompilatorów Javy jest historyczny. Po pierwsze, oryginalne implementacje Javy były oparte na interpreterze, a wydajność była nieistotna. Po drugie, wiele oryginalnych benchmarków Javy było problematycznych do optymalizacji. Przypominam sobie jeden benchmark, który wyglądał bardzo podobnie do twojego drugiego przykładu. Niestety, jeśli kompilator zoptymalizowana pętla away, benchmark otrzyma wyjątek divide by zero, gdy spróbuje podzielić liczbę linii bazowej przez czas, który upłynął, aby obliczyć swój wynik wydajności. Więc pisząc optymalizujący kompilator Javy, trzeba było bardzo uważać, aby nie optymalizować niektórych rzeczy, ponieważ ludzie wtedy twierdzą, że Twój kompilator jest zepsuty.

 1
Author: Chris Dodd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 03:52:47

Optymalizacja takich rzeczy podczas kompilacji do kodu bajtowego JVM jest prawie uważana za złą praktykę. Sun ' s javac ma kilka podstawowych optymalizacji, podobnie jak scalac, groovyc, itd. Krótko mówiąc, wszystko, co jest naprawdę specyficzne dla języka, może zostać zoptymalizowane w kompilatorze. Jednak takie rzeczy, które są oczywiście tak wymyślone, że są agnostyczne językowo, wymkną się po prostu z polityki.

Powodem tego jest to, że pozwala Hotspotowi mieć znacznie bardziej spójny widok na bytecode i jego wzorce. Jeśli Kompilatory zaczną zajmować się przypadkami brzegowymi, zmniejsza to zdolność maszyny Wirtualnej do optymalizacji ogólnego przypadku, który może nie być widoczny w czasie kompilacji. Steve Yeggie lubi o tym mówić: optymalizacja jest często łatwiejsza , gdy jest wykonywana w czasie wykonywania przez sprytną maszynę wirtualną. Posunął się nawet do twierdzenia, że HotSpot usuwa optymalizacje javaca. Chociaż Nie wiem, czy to prawda, nie zdziwiłoby mnie to.

Podsumowując: Kompilatory kierowanie maszyn wirtualnych ma bardzo różny zestaw kryteriów, szczególnie w obszarze optymalizacji i gdy jest to właściwe. Nie obwiniaj autorów kompilatorów za pozostawienie pracy Znacznie bardziej wydajnemu JVM. Jak wielokrotnie podkreślano w tym wątku, nowoczesne Kompilatory kierujące się natywną architekturą (jak rodzina gcc) są niezwykle sprytne, wytwarzając nieprzyzwoicie szybki kod poprzez bardzo inteligentne optymalizacje.

 1
Author: Daniel Spiewak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 04:58:45

Nigdy nie widziałem sensu w eliminacji martwego kodu. Dlaczego programista to napisał?? Jeśli masz zamiar zrobić coś z martwym kodem, zadeklaruj to jako błąd kompilatora! Prawie na pewno oznacza to, że programista popełnił błąd-a w kilku przypadkach nie, właściwą odpowiedzią byłaby dyrektywa kompilatora do używania zmiennej. Jeśli umieszczę martwy kod w rutynie, chcę go wykonać - prawdopodobnie planuję sprawdzić wyniki w debuggerze.

Przypadek, w którym kompilator mógłby zrobić coś dobrego, wyciągając niezmienniki pętli. Czasami clarity mówi, aby zakodować obliczenia w pętli i mieć kompilator wyciągnąć takie rzeczy byłoby dobre.

 1
Author: Loren Pechtel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 05:59:19

Kompilatory, które mogą wykonywać optymalizacje strict-aliasing, zoptymalizują pierwszy przykład. Zobacz tutaj .

Drugi przykład nie może być zoptymalizowany, ponieważ najwolniejszą częścią jest alokacja/realokacja pamięci, a operator + = jest redefiniowany w funkcję, która zajmuje się pamięcią. Różne implementacje łańcuchów wykorzystują różne strategie alokacji.

Ja też wolałbym mieć malloc(100000) niż tysiąc malloc (100) też robiąc s + = "s" ; ale racja teraz to jest poza zasięgiem kompilatorów i musi być zoptymalizowane przez ludzi. To właśnie język D próbuje rozwiązać poprzez wprowadzenie czystych funkcji .

Jak wspomniano tutaj w innych odpowiedziach, perl wykonuje drugi przykład w czasie krótszym niż sekundę, ponieważ przydziela więcej pamięci niż jest to wymagane, na wypadek gdyby więcej pamięci było potrzebne później.

 1
Author: Eugene Bujak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-04 10:20:09

W trybie wydania VS 2010 C++ to nie zajmuje czasu, aby uruchomić. Jednak tryb debugowania to inna historia.

#include <stdio.h>
int main()
{
    int x = 0;
    for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
        x += x + x + x + x + x;
    }
    printf("%d", x);
}
 1
Author: user34537,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-01 04:33:57

Optymalizacja absolutna jest problemem nie do podjęcia decyzji, co oznacza, że nie ma maszyny Turinga (a zatem nie ma programu komputerowego), która mogłaby dać optymalną wersję dowolnego programu.

Niektóre proste optymalizacje mogą być (i w rzeczywistości są) zrobione, ale w przykładach, które podałeś...

  1. Aby wykryć, że twój pierwszy program zawsze wypisuje zero, kompilator musiałby wykryć, że x pozostaje stały pomimo wszystkich iteracji pętli. Jak możesz wyjaśnić (I wiesz, to nie jest najlepsze słowo, ale nie mogę wymyślić innego), że do kompilatora?

  2. Skąd kompilator może wiedzieć, że StringBuilder jest właściwym narzędziem do tego zadania bez żadnego odniesienia do niego?

w aplikacji realnej, jeśli wydajność jest krytyczna w części aplikacji, musi być napisana w języku niskiego poziomu, takim jak C. (Haha, poważnie, napisałem to?)

 1
Author: Eduardo León,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-23 13:53:52

Jest to przykład kodu proceduralnego v. kodu funkcjonalnego.

Masz szczegółową procedurę dla kompilatora, więc optymalizacje będą oparte na szczegółowej procedurze i zminimalizują wszelkie skutki uboczne lub nie zoptymalizują tam, gdzie nie będzie robił tego, czego oczekujesz. Ułatwia to debugowanie.

Jeśli umieścisz Opis funkcjonalny tego, co chcesz np. SQL następnie dajesz kompilatorowi szeroki zakres opcji do optymalizacji.

Być może jakiś rodzaj analizy kodu byłby w stanie znaleźć tego typu problem lub profilowanie w czasie wykonywania, ale wtedy będziesz chciał zmienić źródło na coś bardziej sensownego.

 0
Author: pro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 08:29:35

Ponieważ autorzy kompilatorów starają się dodać optymalizacje dla rzeczy, które mają znaczenie (mam nadzieję) i które są mierzone w * kamiennych benchmarkach (obawiam się).

Istnieją miliony innych możliwych fragmentów kodu, takich jak Twój, które nic nie robią i mogą być zoptymalizowane z rosnącym wysiłkiem kompilatora, ale które prawie nigdy nie są spotykane.

Żenujące jest to, że nawet dzisiaj większość kompilatorów generuje kod, aby sprawdzić, czy wartość przełącznika jest większa niż 255 dla gęstego lub prawie pełny przełącznik na niepodpisanej postaci. To dodaje 2 instrukcje do wewnętrznej pętli większości interpreterów bajtowych.

 0
Author: blabla999,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-17 11:54:04

Nie znoszę mówić o tym na tak starym pytaniu (jak się tu znalazłem?), ale myślę, że część tego może być czymś w rodzaju wstrzymania z czasów Commodore 64.

Na początku lat 80. wszystko działało na stałym zegarze. Nie było Turbo Boostingu, a kod był zawsze tworzony dla konkretnego systemu z określonym procesorem i określoną pamięcią itp. W Commodore BASIC standardowa metoda implementacji delay s wyglądała tak:
10 FOR X = 1 TO 1000
20 NEXT : REM 1-SECOND DELAY

(właściwie, w praktyce, bardziej przypominał 10FORX=1TO1000:NEXT, ale wiesz, co mam na myśli.)

Gdyby to zoptymalizowali, wszystko by się zepsuło-nic nigdy nie byłoby w czasie. Nie znam żadnych przykładów, ale jestem pewien, że w historii skompilowanych języków jest wiele takich małych rzeczy, które uniemożliwiały optymalizację.

Trzeba przyznać, że te nie-optymalizacje nie są dziś konieczne. Prawdopodobnie istnieje jednak jakaś niewypowiedziana zasada wśród twórców kompilatorów nie aby zoptymalizować takie rzeczy. Nie wiem.

Ciesz się, że Twój kod jest nieco zoptymalizowany, W przeciwieństwie do kodu na C64. Wyświetlanie bitmapy na C64 może zająć do 60 sekund przy najbardziej wydajnych pętlach BASIC; tak więc większość gier itp. zostały napisane w języku maszynowym. Pisanie gier w języku maszynowym nie jest zabawne.

Tylko moje myśli.
 0
Author: Tortoise,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-06 03:18:48

Założenie: studiowałem Kompilatory na Uniwersytecie.

Kompilator javac jest bardzo głupi i nie wykonuje absolutnie żadnej optymalizacji, ponieważ opiera się na Java runtime, aby to zrobić. Runtime wyłapie tę rzecz i zoptymalizuje ją, ale wyłapie ją dopiero po wykonaniu funkcji kilka tysięcy razy.

Jeśli użyjesz lepszego kompilatora (takiego jak gcc) umożliwiającego optymalizację, zoptymalizuje to Twój kod, ponieważ jest to dość oczywista optymalizacja.

 0
Author: LtWorf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-23 09:58:17

Kompilatory są tak inteligentne, jak my je tworzymy. Nie znam zbyt wielu programistów, którzy zawracaliby sobie głowę pisaniem kompilatora, który sprawdzałby konstrukcje takie jak te, których używałeś. Większość koncentruje się na bardziej typowych sposobach poprawy wydajności.

Jest możliwe, że pewnego dnia będziemy mieli oprogramowanie, w tym Kompilatory, które może się uczyć i rozwijać. Kiedy ten dzień nadejdzie większość, może wszyscy, programiści będą bez pracy.

 -1
Author: Jim C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-13 20:24:19

Znaczenie twoich dwóch przykładów jest bezcelowe, bezużyteczne i stworzone tylko po to, aby oszukać kompilator.

Kompilator nie jest w stanie (i nie powinien) widzieć znaczenia metody, pętli lub programu. To jest miejsce, gdzie można dostać się do obrazu. Tworzysz metodę dla pewnej funkcjonalności/znaczenia, bez względu na to, jak głupia jest. Tak samo jest w przypadku prostych problemów lub ekstremalnie złożonych programów.

W Twoim przypadku kompilator może go zoptymalizować, ponieważ "myśli" , że powinien być zoptymalizowany w inny sposób, ale po co tam zostać?

Ekstremalna inna sytuacja. Mamy inteligentny kompilator kompilujący Windows. Mnóstwo kodu do kompilacji. Ale jeśli jest mądry, to sprowadza go do 3 linijek kodu...

"starting windows"
"enjoy freecell/solitaire"
"shutting down windows"

Reszta kodu jest przestarzała, ponieważ nigdy nie jest używana, dotykana, dostępna. Naprawdę tego chcemy?

 -1
Author: Mafti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-17 11:58:51

Zmusza cię (programistę) do myślenia o tym, co piszesz. Zmuszanie kompilatorów do pracy za Ciebie nikomu nie pomoże: sprawia, że Kompilatory są znacznie bardziej złożone (i wolniejsze!), a to sprawia, że jesteś głupszy i mniej uważny na swój kod.

 -3
Author: Sophie Alpert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-02 01:09:14