FMA3 w GCC: jak włączyć
Mam i5-4250U, który ma AVX2 i FMA3. Testuję jakiś gęsty kod mnożenia macierzy w GCC 4.8.1 na Linuksie, który napisałem. Poniżej znajduje się lista trzech sposobów kompilacji.
SSE2: gcc matrix.cpp -o matrix_gcc -O3 -msse2 -fopenmp
AVX: gcc matrix.cpp -o matrix_gcc -O3 -mavx -fopenmp
AVX2+FMA: gcc matrix.cpp -o matrix_gcc -O3 -march=native -fopenmp -ffast-math
Wersja SSE2 i AVX wyraźnie różnią się pod względem wydajności. Jednak AVX2 + FMA nie jest lepszy niż wersja AVX. Nie rozumiem tego. Dostaję ponad 80% szczytowych flopów procesora, zakładając, że nie ma FMA, ale myślę, że powinienem być w stanie zrobić dużo lepiej z FMA. Mnożenie macierzy powinno skorzystać bezpośrednio z FMA. Zasadniczo wykonuję osiem produktów kropkowych jednocześnie w AVX. Kiedy sprawdzammarch=native
to daje:
cc -march=native -E -v - </dev/null 2>&1 | grep cc1 | grep fma
...-march=core-avx2 -mavx -mavx2 -mfma -mno-fma4 -msse4.2 -msse4.1 ...
Widzę więc, że jest włączona (żeby się upewnić, że dodałem-mfma
ale to nie robi różnicy).ffast-math
powinien pozwolić na swobodny model zmiennoprzecinkowyJak korzystać z instrukcji Fused Multiply-Add (FMA) z SSE / AVX
Edytować:
Na podstawie komentarzy Mysticial poszedłem dalej i użyłem _mm256_fmadd_ps, a teraz wersja AVX2 + FMA jest szybsza.Nie wiem, dlaczego kompilator nie zrobi tego za mnie. Teraz otrzymuję około 80 GFLOPS (110% szczytów bez FMA) dla ponad 1000x1000 macierzy. W przypadku, gdy ktoś nie wierzy, że moje obliczenie szczytowego flopa jest tutaj, zrobiłem to.
peak flops (no FMA) = frequency * simd_width * ILP * cores
= 2.3GHZ * 8 * 2 * 2 = 73.2 GFLOPS
peak flops (with FMA) = 2 * peak flops (no FMA) = 146.2 GFLOPS
Mój procesor w trybie turbo przy użyciu obu rdzeni wynosi 2,3 GHz. Dostaję 2 za ILP, ponieważ Ivy Bridge może wykonać jedno mnożenie AVX i jeden dodatek AVX w tym samym czasie (rozwinąłem pętlę kilka razy, aby to zapewnić).
Dostaję tylko około 55% szczytowych flopów (z FMA). Nie wiem dlaczego, ale przynajmniej teraz coś widzę.
Jednym ze skutków ubocznych jest to, że teraz otrzymuję mały błąd, gdy porównuję się do prostego algorytmu mnożenia macierzy, który wiem, że ufam. Myślę, że jest to spowodowane faktem, że FMA ma tylko jeden tryb zaokrąglania zamiast normalnie dwóch (co ironicznie łamie reguły zmiennoprzecinkowe IEEE, chociaż prawdopodobnie jest lepsze).
Edytować:
Ktoś musi powtórzyćJak osiągnąć teoretyczne maksimum 4 FLOPów na cykl? ale wykonaj 8 podwójnych zmiennoprzecinkowych FLOPS na cykl z Haswellem.
Edytować
W rzeczywistości Mysticial zaktualizował swój projekt do obsługi FMA3 (zobacz jego odpowiedź w powyższym linku). Uruchomiłem jego kod w Windows8 z MSVC2012 (ponieważ wersja Linuxa nie skompilowała się z obsługą FMA). Oto wyniki.
Testing AVX Mul + Add:
Seconds = 22.7417
FP Ops = 768000000000
FLOPs = 3.37705e+010
sum = 17.8122
Testing FMA3 FMA:
Seconds = 22.1389
FP Ops = 1536000000000
FLOPs = 6.938e+010
sum = 333.309
To 69,38 GFLOPS dla FMA3 dla podwójnego zmiennoprzecinkowego. Dla pojedynczego zmiennoprzecinkowego muszę go podwoić, tak że wynosi 138.76 SP GFLOPS. Obliczam mój szczyt to 146,2 SP GFLOPS.To 95% szczytu! Innymi słowy, powinienem być w stanie trochę poprawić mój kod GEMM (chociaż jest już trochę szybszy niż Eigen).