Czy stała poprawność może poprawić wydajność?

92

Wielokrotnie czytałem, że wymuszanie poprawności const w kodzie C lub C ++ jest nie tylko dobrą praktyką w odniesieniu do łatwości utrzymania, ale może również pozwolić kompilatorowi na wykonanie optymalizacji. Jednak przeczytałem też zupełnie odwrotnie - że w ogóle nie wpływa to na wydajność.

W związku z tym, czy masz przykłady, w których poprawność const może pomóc kompilatorowi w poprawieniu wydajności programu?

shuhalo
źródło
50
Stała poprawność jest jedną z NAJLEPSZYCH praktyk w zakresie konserwacji. Jeśli twój kod C ++ nie jest stały, jest to po prostu kupa bzdur czekająca na katastrofę. Nie ma to wpływać na wydajność.
2
@Neil Butterworth: niestety odwrotność nie jest prawdą.
Beta
6
Oto przykład, w którym constnastąpiła różnica w wydajności: stackoverflow.com/questions/1121791/… . Zasadniczo była to jednak kwestia jakości wdrożenia. constnie określił, czy kompilator może legalnie dokonać optymalizacji, po prostu zdarzyło się, że wersja kompilatora nie wykonała tego, gdy jej brakowało.
Steve Jessop
3
Jestem całkiem pewien, że „morgennebel” pominęło „tylko” w pierwszym zdaniu: ma to o wiele większy sens, gdy „to nie tylko dobra praktyka”.
IanH
2
@IanH Tak, rozważałem to. Ale PO miał wystarczająco dużo czasu na wyjaśnienie. Ludzie, którzy zadają pytania i po prostu znikają, naprawdę mnie wkurzają.

Odpowiedzi:

77

constPoprawność nie może poprawić wydajność, ponieważ const_casti mutablesą w języku i pozwól kod do conformingly łamać zasady. Jest to jeszcze gorsze w C ++ 11, gdzie twoje constdane mogą np. Być wskaźnikiem do a std::atomic, co oznacza, że ​​kompilator musi uwzględniać zmiany wprowadzone przez inne wątki.

To powiedziawszy, dla kompilatora jest trywialne, aby spojrzeć na kod, który generuje, i określić, czy faktycznie zapisuje do danej zmiennej, i odpowiednio zastosować optymalizacje.

To powiedziawszy, constpoprawność to dobra rzecz w odniesieniu do łatwości konserwacji. W przeciwnym razie klienci Twojej klasy mogą złamać wewnętrznych członków tej klasy. Weźmy na przykład pod uwagę standard std::string::c_str()- gdyby nie mógł zwrócić wartości stałej, byłbyś w stanie poradzić sobie z wewnętrznym buforem łańcucha!

Nie używaj constze względu na wydajność. Używaj go ze względów konserwacyjnych.

Billy ONeal
źródło
31
"byłbyś w stanie poradzić sobie z wewnętrznym buforem łańcucha!" - co najważniejsze, byłbyś w stanie przypadkowo wkręcić wewnętrzny bufor. Błędy kompilatora spowodowane constsą drogowskazami z napisem „robisz coś głupiego”.
Steve Jessop
4
... a odlewy const to drogowskazy z napisem „autor tego kodu próbuje zrobić coś sprytnego” ;-)
Steve Jessop
5
@Steve Jessop - lub const-cast to drogowskazy z napisem: „Próbuję podbić zbiór kodu z poprawną stałą do kodu niepoprawnego dla stałej i nie mogę naprawić żadnego z nich”. Co, powiem ci, nie jest w żaden sposób sprytne, po prostu denerwujące.
Michael Kohne
7
@Michael - tak, słuszna uwaga. Być może oryginalny drogowskaz nie jest „robisz coś głupiego”, a raczej „ktoś robi coś głupiego”.
Steve Jessop
Godbolt i Arduino powiedzieli mi, że poprawność const to nie tylko zabawa.
gratrz
31

Tak, może.

Większość z nich constjest wyłącznie dla korzyści programisty i nie pomaga kompilatorowi w optymalizacji, ponieważ ich odrzucenie jest legalne, a więc nie mówią kompilatorowi niczego przydatnego do optymalizacji. Jednak niektórych constplików nie można (zgodnie z prawem) odrzucić i dostarczają one kompilatorowi przydatnych informacji do optymalizacji.

Na przykład dostęp do zmiennej globalnej zdefiniowanej za pomocą consttypu może być wbudowany, podczas gdy nie można wstawić tej bez consttypu, ponieważ może się to zmienić w czasie wykonywania.

https://godbolt.org/g/UEX4NB

C ++:

int foo1 = 1;
const int foo2 = 2;

int get_foo1() {
    return foo1;
}

int get_foo2() {
    return foo2;
}

jako M:

foo1:
        .long   1
foo2:
        .long   2
get_foo1():
        push    rbp
        mov     rbp, rsp
        mov     eax, DWORD PTR foo1[rip] ; foo1 must be accessed by address
        pop     rbp
        ret
get_foo2():
        push    rbp
        mov     rbp, rsp
        mov     eax, 2 ; foo2 has been replaced with an immediate 2
        pop     rbp
        ret

W praktyce należy pamiętać, że chociaż constmoże poprawić wydajność, w większości przypadków nie będzie lub będzie, ale zmiana nie będzie zauważalna. Podstawową użytecznością constnie jest optymalizacja.


Steve Jessop podaje inny przykład w swoim komentarzu do pierwotnego pytania, w którym pojawia się coś, o czym warto wspomnieć. W zakresie blokowym kompilator może wywnioskować, czy zmienna zostanie zmutowana i odpowiednio zoptymalizować, niezależnie od tego const, ponieważ kompilator może zobaczyć wszystkie zastosowania zmiennej. W przeciwieństwie do powyższego przykładu, nie można przewidzieć, czy foo1zostanie zmutowany, ponieważ można go zmodyfikować w innych jednostkach tłumaczeniowych. Przypuszczam, że hipotetyczny, czujący ultra-kompilator mógłby przeanalizować cały program i określić, czy można go uzyskać w trybie inline foo1... ale prawdziwe kompilatory nie mogą.

Prakseolityczny
źródło
@ericcurtin Dlatego nie wspomniałem o kompilatorze w odpowiedzi. Zwykle podczas publikowania wygenerowanego zestawu upewniłbym się, że podam kompilator i wersję, ale jest to optymalizacja, którą wykona każdy główny kompilator optymalizujący, więc nie chciałem sprawiać wrażenia, że ​​dotyczyło to tylko jednego kompilatora.
Praxeolitic
1
@Acorn Oto ten sam przykład, ale z obiektem klasy: godbolt.org/z/R-Zfgc . Ponadto zmienne w przykładzie mają powiązania zewnętrzne.
Praxeolitic
6

z mojego doświadczenia, nie

W przypadku zmiennych skalarnych kompilator jest w stanie określić każdą zmianę wartości i samodzielnie przeprowadzić niezbędną optymalizację.

W przypadku wskaźników tablicowych poprawność const nie gwarantuje, że wartości są naprawdę stałe w przypadku potencjalnych problemów z aliasowaniem. Dlatego kompilator nie może używać samego modyfikatora const do przeprowadzania optymalizacji

jeśli szukasz optymalizacji, powinieneś rozważyć __restrict__specjalne modyfikatory / atrybuty funkcji: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

Anycorn
źródło