2014-12-04 11 views
16

Jeśli robięW C++, czy mogę zadeklarować odniesienie, aby wskazać, że nic go nie zmieni?

typedef void Cb(); 

int foo(int const& a, Cb cb) { 
    int x = a; 
    cb(); 
    return x - a; 
} 

i skompilować z g++ -O3 -save-temps -c foo.cpp, widzę, że odejmowanie jest zachowana, natomiast jeśli cb(); jest wypowiedziało się, cała funkcja optymalizuje do

xorl %eax, %eax 

Czy jest coś, co mogę zrobić do specyfikacji parametru a, tak aby odejmowanie zostało zoptymalizowane bez względu na wywołanie do cb() i bez wymuszania, aby a było unikalnym odnośnikiem (tj. że może być odniesione gdzie indziej, ale czy przez żadne z tych odniesień nie zostanie zmieniony)?

+11

Możesz wziąć parametr według wartości ('int a'), który zapewni kompilatorowi, że nic nie może go zmienić. Nie jestem pewien, co masz na myśli przez "unikalne odniesienie". – cdhowie

+0

co dzieje się ze znakiem const int x = a? –

+1

@pepper_chico 'a' nadal może być odniesieniem do czegoś, co potencjalnie zmienia się przez wywołanie' cb() ', więc odejmowanie wciąż nie może być zoptymalizowane. – Snps

Odpowiedz

15

Jest rozszerzenie __restrict, można spróbować to na gcc.godbolt.org:

typedef void Cb(); 

int foo(const int & __restrict a, Cb cb) { 
    int x = a; 
    cb(); 
    return x - a; 
} 

ciekawe, only clang does the optimization, gcc doesn't do it.


Wskazówka które ograniczają podobny aliasingu jest uważana za część standardu C++:

Może w przyszłości można to zrobić przez normę.

+0

Myślę, że 'restrict' nie działa tutaj: mówi tylko, że dwa odniesienia w danym zakresie bloku nie mają charakteru aliasu. Problem polega na tym, że 'a' może mieć alias ze zmienną globalną, a' limit' nie może być używane dla globów. Zobacz także: http://stackoverflow.com/a/30827880/895245 –

15

Doing sugerowanej optymalizacji byłoby niewłaściwe, ponieważ reszta kodu może być:

static int var; 

void func() 
{ 
    var++; 
} 

// ... 
foo(var, func); 

nie jestem świadomy jakiegokolwiek atrybutu kompilatora specyficzne można ustawić, aby powiedzieć, że cb() nie będą modyfikować a.

+3

Obejście problemu polegałoby na przyjęciu 'a' przez wartość lub obliczeniu wartości zwracanej przed wywołaniem' cb' –

+0

'__attribute __ ((const)) wydaje się działać: http://stackoverflow.com/a/31180833/895245 –

5

Można użyć C restrict na referencji, jeśli twój kompilator obsługuje to rozszerzenie.
(Niektóre kompilator pozwoli __restrict lub __restrict__, które są częścią nazw wdrożeń.)

To obietnica od was do kompilatora, że ​​obiekt nie jest aliasem dowolnym miejscu i może w ten sposób zoptymalizować go.
Jeśli okłamałeś kompilator, otrzymasz złamany kod, na który zasługujesz.

+0

Dziękuję, to jest użyteczna rzecz wiedzieć. Wydaje się, że specyfikacja 'ograniczenia', jeśli rozumiem poprawnie, wymaga nie tylko tego, że' a' nie będzie modyfikowana gdzie indziej, ale że nie będzie również nigdzie indziej. Idealnie chciałbym, aby "a" mogło mieć wiele innych odniesień, z których żaden nie zmieniałby obiektu leżącego pod nim. – Owen

+1

Cóż, iff obiekt nie jest modyfikowany przez żadne z nich, tak naprawdę nie ma znaczenia, że ​​istnieją aliasy. Przynajmniej dla poprawności programów. – Deduplicator

+0

Jeśli dobrze rozumiem [tę odpowiedź] (http://stackoverflow.com/a/745877/371739), może to mieć znaczenie. – Owen

6

Dlaczego po prostu nie przesunąć linii int x = a; poniżej wywołania funkcji?

Jeśli cb() wpływy ani x ani a, powinno być w porządku to robić, i myślę, że kompilator będzie ponownie optymalizacji połączenia, ponieważ x nie może zmieniać się między dwoma połączeniami. Jeśli istnieje powód, dla którego nie można zmienić kolejności tych dwóch połączeń, prawdopodobnie nie można go zoptymalizować.

Nie jest to jednak coś, co można wskazać kompilatorowi, ponieważ nie można zagwarantować, że ani x, ani nie zmieniły się po wywołaniu cb().

Pomyśl o tym jako o zamówieniu dostępu do odczytu/zapisu. Jeśli podczas cb() nie zostanie odczytany ani zapisany dostęp do a, można ręcznie zmienić kolejność wywołania funkcji.

Jeśli zostanie napisane a lub x, nie można zmienić kolejności, a optymalizacja nie będzie poprawna.

Jeśli x jest czytany, nie można zmienić kolejność wywołania funkcji, ale jeśli x naprawdę jest tylko do odczytu, można odczytać z zamiast, i określić x dopiero po zakończeniu rozmowy, ponieważ będzie mieć taką samą wartość jak a, miał cię zadeklarował go przed wywołaniem.

-1

__attribute __ ((const)) wystarczy

Jak udokumentowano w: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Function-Attributes.html, to informuje kompilator, że dana funkcja nie modyfikuje globalnych (choć można je odczytać).

Istnieje również podzbiór pure z const, który również zabrania odczytów globalnych.

Rozważmy następujący uproszczony przykład:

int __attribute__((const)) g(); 

int f(int a) { 
    int x = a; 
    g(); 
    return x - a; 
} 

Na g++ 4,8 x86_64 -O3, kompiluje do:

  • xor %eax,%eax z const
  • duży funkcja, która nie przypuszczam a nie jest modyfikowana bez const

Wygląda na to, że nie ma żadnego dokładniejszego atrybutu, który mówi, że funkcja nie modyfikuje zmiennej podanej, zgodnie z wymaganiami.

Czy __attribute __ ((const)) można zastosować do wskaźników funkcji?

spróbujmy:

int f(int& a, void __attribute__((const)) (*g)(void)) { 
    int x = a; 
    (*g)(); 
    return x - a; 
} 

Ponownie kompiluje do xor %eax,%eax oraz dużej funkcji bez const, więc odpowiedź brzmi: tak.

składnia został poproszony na: Function pointer to __attribute__((const)) function?

pojawiła się również w 2011 roku na liście dyskusyjnej pod adresem: http://comments.gmane.org/gmane.comp.gcc.help/38052 W czasie przynajmniej to działało tylko dla niektórych atrybutów.

Czy __attribute __ ((const)) można dodać do typedef?

to działa:

typedef void __attribute__((const)) (*g_t)(void); 

int f(int& a, g_t g) { 
    int x = a; 
    (*g)(); 
    return x - a; 
} 

czyli

typedef void (g_t)(void); 

int f(int& a, g_t __attribute__((const)) g) { 
    int x = a; 
    g(); 
    return x - a; 
} 

Ale nie mogłem znaleźć drogę do zarówno umieścić atrybut na typedef i przekazać funkcję, nie wskaźnik , np .:

typedef void __attribute__((const)) (g_t)(void); 

GCC ostrzega, że W tym przypadku atrybut został zignorowany.

+0

@Warvoters proszę wyjaśnić, więc mogę uczyć się i poprawić informacje. –