2013-01-24 10 views
16

Say miałem ten fragment kodu:Czy kompilatory automatycznie optymalizują powtarzające się połączenia z funkcjami matematycznymi?

#include <cmath> 

// ... 

float f = rand(); 
std::cout << sin(f) << " " << sin(f); 

Jak sin(f) jest dobrze określoną funkcję nie jest łatwym optymalizacja:

float f = rand(); 
float sin_f = sin(f); 
std::cout << sin_f << " " << sin_f; 

Czy to optymalizacja, że ​​to rozsądne, aby oczekiwać nowoczesny kompilator C++ zrobić sam? Czy też nie ma sposobu, aby kompilator ustalił, że sin(f) powinien zawsze zwracać tę samą wartość dla równej wartości f?

+2

O ile "sin" nie jest zdefiniowany w tej samej jednostce kompilacji, kompilator nie wie, w jaki sposób "grzech" jest zaimplementowany, więc w najlepszym przypadku stanie się to w czasie połączenia. – Thomas

+2

@Thomas Niekoniecznie. Kilka kompilatorów traktuje funkcje niektórych nazw specjalnie, ponieważ wiedzą, że są zdefiniowane w standardowej bibliotece, a standard gwarantuje coś na ich temat. Istnieją również specyficzne dla kompilatora atrybuty funkcji, które zdefiniowane przez użytkownika nagłówki mogą używać do deklarowania siebie jako czystych, chociaż nie wiem, czy ta wiedza służy do optymalizacji. – delnan

+0

Optymalizacja rzeczywiście odbywa się za pomocą gcc. –

Odpowiedz

18

Korzystanie g ++ zbudowany z flagami domyślny optymalizacja:

float f = rand(); 
40117e: e8 75 01 00 00   call 4012f8 <_rand> 
401183: 89 44 24 1c    mov %eax,0x1c(%esp) 
401187: db 44 24 1c    fildl 0x1c(%esp) 
40118b: d9 5c 24 2c    fstps 0x2c(%esp) 
std::cout << sin(f) << " " << sin(f); 
40118f: d9 44 24 2c    flds 0x2c(%esp) 
401193: dd 1c 24    fstpl (%esp) 
401196: e8 65 01 00 00   call 401300 <_sin> <----- 1st call 
40119b: dd 5c 24 10    fstpl 0x10(%esp) 
40119f: d9 44 24 2c    flds 0x2c(%esp) 
4011a3: dd 1c 24    fstpl (%esp) 
4011a6: e8 55 01 00 00   call 401300 <_sin> <----- 2nd call 
4011ab: dd 5c 24 04    fstpl 0x4(%esp) 
4011af: c7 04 24 e8 60 40 00 movl $0x4060e8,(%esp) 

zbudowany z -O2:

float f = rand(); 
4011af: e8 24 01 00 00   call 4012d8 <_rand> 
4011b4: 89 44 24 1c    mov %eax,0x1c(%esp) 
4011b8: db 44 24 1c    fildl 0x1c(%esp) 
std::cout << sin(f) << " " << sin(f); 
4011bc: dd 1c 24    fstpl (%esp) 
4011bf: e8 1c 01 00 00   call 4012e0 <_sin> <----- 1 call 

Od to widzimy, że bez optymalizacji kompilator używa 2 wywołań i tylko 1 z optymalizacją, empirycznie, jak sądzę, możemy powiedzieć, że kompilator optymalizuje t dzwoni.

17

jestem dość pewne znaki GCC sin z niestandardowym atrybutem czystego, tj __attribute__ ((pure));

ten ma następujący efekt:

Wiele funkcji nie mają skutków oprócz wartości zwracanej i ich powrotu wartość zależy tylko od parametrów i/lub zmiennych globalnych. Taka funkcja może być przedmiotem wspólnej eliminacji wyrażeń i optymalizacji pętli, tak jak byłoby to możliwe w przypadku operatora arytmetycznego.

http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

A więc istnieje bardzo duże prawdopodobieństwo, że takie czyste połączenia zostaną zoptymalizowane eliminacja wspólnych podwyrażeń.

(aktualizacja: faktycznie cmath korzysta constexpr, co oznacza te same optymalizacje)

+0

Po prostu, aby dodać do tego, MSC wie o kilku takich funkcjach i może generować odpowiedni kod w linii. –

+0

'constexpr' ma _nie_ imply pure. Możliwe jest zaimplementowanie zgodnego 'constexpr', który losowo zgłasza wyjątki dla pewnych danych wejściowych. –

+1

gcc również optymalizuje, jeśli włączysz tylko , który nie ma atrybutu, wie o nim wewnętrznie, i zaznacza grzech jako "const", chyba że przerzucisz -frounding-mat, gdzie jest tylko "czysty". –

Powiązane problemy