Jaki rodzaj optymalizacji oferuje const w C / C++? (jeśli w ogóle)

Wiem, że w miarę możliwości należy używać słowa kluczowego const podczas przekazywania parametrów przez referencję lub wskaźnik ze względu na czytelność. Czy są jakieś optymalizacje, które kompilator może zrobić, jeśli podam, że argument jest stały?

Może być kilka przypadków:

Parametry funkcji:

Stałe odniesienie:

void foo(const SomeClass& obj)

Stała obiektu SomeClass:

void foo(const SomeClass* pObj)

I stały wskaźnik do SomeClass:

void foo(SomeClass* const pObj)

Zmienna deklaracje:

const int i = 1234

Deklaracje funkcji:

const char* foo()

Jaki rodzaj optymalizacji kompilatora oferuje każdy z nich (jeśli w ogóle)?

Author: UnTraDe, 2014-12-14

5 answers

[czytelnicy proszę zwrócić uwagę, że większość tego postu została usunięta z artykułu autorstwa Herba Suttera- http://www.gotw.ca/gotw/081.htm - bez przypisania przez OP.]

CASE_1:-

Kiedy deklarujesz const w swoim programie,

int const x = 2;

Kompilator może zoptymalizować ten const, nie dostarczając pamięci do tej zmiennej, a dodając ją do tabeli symboli. Tak więc, kolejne odczyty wymagają tylko indrection do tabeli symboli, a nie instrukcji pobierania wartości z pamięci.

UWAGA: - jeśli zrobisz coś takiego jak poniżej: -

const int x = 1;
const int* y = &x;

To wymusiłoby na kompilatorze przydzielenie miejsca dla 'x'. Tak więc, ten stopień optymalizacji nie jest możliwy w tym przypadku.

Pod względem parametrów funkcji const oznacza, że parametr nie jest modyfikowany w funkcji. Z tego, co wiem, nie ma znaczącego wzrostu wydajności dla korzystania const raczej jest to środek do zapewnienia poprawności.

CASE_2:-

"czy deklarując parametr i / lub wartość zwracana jako const pomagają kompilatorowi wygenerować bardziej optymalny kod?"

  const Y& f( const X& x )
  {
    // ... do something with x and find a Y object ...
    return someY;
  }

Ques= > co kompilator mógłby zrobić lepiej?

=> czy można uniknąć kopii parametru lub wartości zwracanej?

Nie, ponieważ argument jest już przekazywany przez odniesienie.

=> czy można umieścić kopię x lub someY w pamięci tylko do odczytu?

Nie, ponieważ zarówno x, jak i someY żyją poza jego zasięgiem i pochodzą z i / lub są podane do świat zewnętrzny. Nawet jeśli someY jest dynamicznie przydzielane w locie wewnątrz samej F (), to on i jego własność są oddawane do wywołującego.

Ques= > a co z możliwymi optymalizacjami kodu, który pojawia się wewnątrz ciała f()? Z powodu const, czy kompilator mógłby w jakiś sposób poprawić kod, który generuje dla ciała f ()?

Nawet gdy wywołujesz funkcję członka const, kompilator nie może założyć, że bity obiektu x lub obiektu someY nie zostaną zmienione. Dalsze, istnieją dodatkowe problemy( chyba że kompilator wykona globalną optymalizację): kompilator może również nie wiedzieć na pewno, że żaden inny kod nie może mieć odniesienia non-const, które aliasują ten sam obiekt Co x i / lub someY, i czy jakiekolwiek takie odniesienia non-const do tego samego obiektu mogą zostać użyte przypadkowo podczas wykonywania f (); a kompilator może nawet nie wiedzieć, czy rzeczywiste obiekty, do których x i someY są tylko odniesieniami, zostały faktycznie zadeklarowane const w pierwszej kolejności. miejsce.

CASE_3:-

  void f( const Z z )
  {
    // ...
  }

Ques= > czy będzie w tym jakaś optymalizacja?

Tak ponieważ kompilator wie, że z naprawdę jest obiektem const, może przeprowadzić kilka użytecznych optymalizacji nawet bez globalnej analizy. Na przykład, jeśli ciało f() zawiera wywołanie takie jak g( &z ), kompilator może być pewien, że nie-mutowalne części z nie zmieniają się podczas wywołania g()

 24
Author: ravi,
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
2016-04-14 08:47:22

Przed udzieleniem jakiejkolwiek odpowiedzi, chcę podkreślić, że powodem używania lub nie używania const naprawdę powinno być poprawność programu i jasność dla innych programistów, bardziej niż dla optymalizacji kompilatora; to jest, dokonywanie parametru const dokumenty, że metoda nie będzie modyfikować tego parametru, a dokonywanie funkcji member const dokumenty, że ten członek nie będzie modyfikować obiektu, którego jest członkiem (przynajmniej nie w sposób logiczny zmieniający wyjście z każdego innego const funkcja członka). Pozwala to na przykład programistom uniknąć tworzenia niepotrzebnych kopii obiektów (ponieważ nie muszą się martwić, że oryginał zostanie zniszczony lub zmodyfikowany) lub uniknąć niepotrzebnej synchronizacji wątków(np. wiedząc, że wszystkie wątki po prostu czytają i nie mutują danego obiektu).

Pod względem optymalizacji kompilator może dokonać, przynajmniej teoretycznie, choć w trybie optymalizacji, który pozwala mu na pewne niestandardowe założenia to może złamać standardowy kod C++, rozważ:

for (int i = 0; i < obj.length(); ++i) {
   f(obj);
}

Załóżmy, że funkcja length jest oznaczona jako const, ale w rzeczywistości jest kosztowną operacją(powiedzmy, że faktycznie działa w czasie O (n) zamiast O(1)). Jeśli funkcja f przyjmuje swój parametr za pomocą referencji const, kompilator może potencjalnie zoptymalizować tę pętlę do:

int cached_length = obj.length();
for (int i = 0; i < cached_length; ++i) {
   f(obj);
}

... ponieważ fakt, że funkcja f nie modyfikuje parametru gwarantuje, że funkcja length zwróci ten sam wartości za każdym razem, gdy obiekt się nie zmienił. Jednakże, Jeśli f jest zadeklarowane, aby przyjmować parametr przez zmienną referencję, to length musiałby być ponownie obliczony przy każdej iteracji pętli, ponieważ f mógł zmodyfikować obiekt w sposób powodujący zmianę wartości.

Jak zaznaczono w komentarzach, zakłada to szereg dodatkowych zastrzeżeń i będzie możliwe tylko przy wywołaniu kompilatora w niestandardowym trybie, który pozwala mu na wykonanie dodatkowych założenia (takie jak to, że metody const są ściśle funkcją ich danych wejściowych i że optymalizacje mogą zakładać, że kod nigdy nie użyje const_cast do konwersji parametru referencyjnego const do zmiennego odniesienia).

 11
Author: Michael Aaron Safyan,
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
2016-11-29 08:57:58

Parametry funkcji:

const nie ma znaczenia dla pamięci odniesienia. To jak Wiązanie ręki za plecami optymalizatora.

Załóżmy, że wywołujesz inną funkcję (np. void bar()) w foo, która nie ma widocznej definicji. Optymalizator będzie miał ograniczenie, ponieważ nie ma możliwości sprawdzenia, czy bar zmodyfikował parametr funkcji przekazany do foo (np. poprzez dostęp do pamięci globalnej). Możliwość modyfikacji pamięci na zewnątrz i aliasingu wprowadzenie istotnych ograniczeń dla optymalizatorów w tym obszarze.

Chociaż nie pytałeś, const wartości dla parametrów funkcji pozwalają na optymalizację, ponieważ optymalizator ma zagwarantowany obiekt const. Oczywiście koszt skopiowania tego parametru może być znacznie wyższy niż korzyści optymalizatora.

Zobacz: http://www.gotw.ca/gotw/081.htm


Deklaracje zmiennych: const int i = 1234

To zależy gdzie jest zadeklarowane, kiedy jest tworzony, oraz Typ. Ta kategoria jest w dużej mierze tam, gdzie istnieją optymalizacje const. Modyfikowanie const obiektu lub znanej stałej jest niezdefiniowane, więc kompilator może dokonać pewnych optymalizacji; zakłada, że nie wywołujesz niezdefiniowanego zachowania i wprowadza pewne gwarancje.

const int A(10);
foo(A);
// compiler can assume A's not been modified by foo

Oczywiście, optymalizator może również zidentyfikować zmienne, które się nie zmieniają:

for (int i(0), n(10); i < n; ++i) { // << n is not const
 std::cout << i << ' ';
}

Deklaracje funkcji: const char* foo()

Nieistotne. Pamięć odniesienia może być modyfikowana zewnętrznie. Jeśli zmienna zwrócona przez foo jest widoczna, to optymalizator może dokonać optymalizacji, ale nie ma to nic wspólnego z obecnością/brakiem const na typie zwracanym przez funkcję.

Ponownie, const wartość lub obiekt jest inny:

extern const char foo[];

 6
Author: justin,
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-12-15 05:23:57

SomeClass* const pObj tworzy stały obiekt typu wskaźnik. Nie istnieje bezpieczna metoda zmiany takiego obiektu, więc kompilator może na przykład buforować go do rejestru z odczytem tylko jednej pamięci, nawet jeśli jego adres jest brany.

Pozostałe nie włączają żadnych optymalizacji, chociaż kwalifikator const w typie wpłynie na rozdzielczość przeciążenia i prawdopodobnie spowoduje wybranie różnych i szybszych funkcji.

 3
Author: Ben Voigt,
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-12-14 05:34:06

Dokładne efekty const różnią się dla każdego kontekstu, w którym jest używany. Jeśli const jest używany podczas deklarowania zmiennej, jest fizycznie const i znajduje się w pamięci tylko do odczytu.

const int x = 123;

Próba odrzucenia const - ness jest nieokreślona:

Chociaż const_cast może usuwać stałość lub zmienność z dowolnego wskaźnika lub odniesienia, używając wynikowego wskaźnika lub odniesienia do zapisu do obiektu, który został zadeklarowany const lub aby uzyskać dostęp do obiektu, który został zadeklarowana zmienna wywołuje nieokreślone zachowanie. cppreference / const_cast

Więc w tym przypadku kompilator może przyjąć, że wartość x jest zawsze 123. Otwiera to pewien potencjał optymalizacji (propagacja stałych)

Dla funkcji to inna sprawa. Przypuszczać:

void doFancyStuff(const MyObject& o);

Nasza funkcja {[5] } może wykonać dowolną z następujących czynności z o.

  1. nie modyfikować obiektu.
  2. Odrzuć konsternację, a następnie zmodyfikuj obiekt
  3. modify an mutable data member of MyObject

Zauważ, że jeśli wywołasz naszą funkcję z instancją MyObject, która została zadeklarowana jako const, wywołasz niezdefiniowane zachowanie za pomocą #2.

Pytanie Guru: czy poniższe wywołują nieokreślone zachowanie?

const int x = 1;
auto lam = [x]() mutable {const_cast<int&>(x) = 2;};
lam();
 3
Author: Stefan,
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-12-14 15:55:54