Pytanie Dlaczego kompilator generuje dodatkowe sqrty w skompilowanym kodzie zespołu


Próbuję profilować czas potrzebny do obliczenia sqrt za pomocą następującego prostego kodu C, gdzie readTSC () jest funkcją do odczytu licznika cykli procesora.

double sum = 0.0;
int i;
tm = readTSC();
for ( i = 0; i < n; i++ )
   sum += sqrt((double) i);
tm = readTSC() - tm;
printf("%lld clocks in total\n",tm);
printf("%15.6e\n",sum);

Jednak, jak wydrukowałem kod montażu za pomocą

gcc -S timing.c -o timing.s

na komputerze Intela wynik (pokazany poniżej) był zaskakujący?

Dlaczego w kodzie złożenia są dwa sqrty, z których jeden korzysta z sqrtsd instrukcja, a druga za pomocą wywołania funkcji? Czy jest to związane z rozwijaniem pętli i próbą wykonania dwóch sqrts w jednej iteracji?

I jak zrozumieć linię

ucomisd %xmm0, %xmm0

Dlaczego to się porównuje %xmm0 Do siebie?

//----------------start of for loop----------------
call    readTSC
movq    %rax, -32(%rbp)
movl    $0, -4(%rbp)
jmp .L4
.L6:
cvtsi2sd    -4(%rbp), %xmm1
// 1. use sqrtsd instruction
sqrtsd  %xmm1, %xmm0
ucomisd %xmm0, %xmm0
jp  .L8
je  .L5
.L8:
movapd  %xmm1, %xmm0
// 2. use C funciton call
call    sqrt
.L5:
movsd   -16(%rbp), %xmm1
addsd   %xmm1, %xmm0
movsd   %xmm0, -16(%rbp)
addl    $1, -4(%rbp)
.L4:
movl    -4(%rbp), %eax
cmpl    -36(%rbp), %eax
jl  .L6
//----------------end of for loop----------------
call    readTSC

14
2018-04-24 17:51


pochodzenie


To musi być niezoptymalizowany kod. Prawdziwy kod prawidłowo układa gałęzie (bez branych gałęzi na przypadek nie-NaN) i znika je ponieważ zawsze będzie to prawda po ucomisd. - Peter Cordes


Odpowiedzi:


Korzysta z biblioteki sqrt funkcja do obsługi błędów. Zobacz dokumentację glibc: 20.5.4 Raportowanie błędów za pomocą funkcji matematycznych: zestaw funkcji matematycznych errno dla zgodności z systemami, które nie mają flag wyjątków IEEE754. Powiązane: glibc's math_error(7) strona man.

Jako optymalizacja najpierw próbuje wykonać pierwiastek kwadratowy przez inline sqrtsd instrukcja, a następnie sprawdza wynik przed samym użyciem przy użyciu ucomisd instrukcja, która ustawia flagi w następujący sposób:

CASE (RESULT) OF
   UNORDERED:    ZF,PF,CF  111;
   GREATER_THAN: ZF,PF,CF  000;
   LESS_THAN:    ZF,PF,CF  001;
   EQUAL:        ZF,PF,CF  100;
ESAC;

W szczególności porównanie a QNaN do siebie powróci UNORDERED, co otrzymasz, jeśli spróbujesz pobrać pierwiastek kwadratowy z liczby ujemnej. Obejmuje to jp gałąź. The je sprawdzenie to po prostu paranoja, sprawdzająca dokładną równość.


Zauważ również, że gcc ma -fno-math-errno opcja który poświęci tę obsługę błędów dla prędkości. Ta opcja jest częścią -ffast-math, ale można go używać samodzielnie bez włączania jakichkolwiek optymalizujących wyniki optymalizacji.

sqrtsd sam prawidłowo tworzy NaN dla wejść ujemnych i NaN oraz ustawia flagę IEEE754 Invalid. Czek i oddział to tylko zachować errno- semantyka, na której większość kodu nie polega.

-fno-math-errno jest domyślnym ustawieniem dla Darwin (OS X), gdzie biblioteka matematyczna nigdy się nie ustawia errno, więc funkcje można ustawić bez tej kontroli.


23
2018-04-24 17:58



Zauważ, że `-ffast-math 'robi więcej niż tylko poświęcenie obsługi błędów dla szybkości. W szczególności łamie ono również zgodność z IEEE 754, tj. Należy zachować ostrożność i tylko wtedy, gdy wiesz, co robisz. Zobacz też stackoverflow.com/questions/7420665/... - godfatherofpolka
@godfatherofpolka tak, ogólnie. Jednak w tym przypadku to wszystko, co robi. - Jester
tak, to prawda, po prostu mam wrażenie, że każda wzmianka na temat flagi szybkiej matematyki powinna nosić etykietę ostrzegawczą, dlatego dodałem ten komentarz. - godfatherofpolka
@godfatherofpolka fair wystarczająco :) - Jester
@harold: -fno-math-errno eliminuje test i jest bezpieczniejszy. - rici