Ich benutze Intel SSE/AVX/FMA intrinsics, um perfekt inline SSE/AVX Anweisungen für einige mathematische Funktionen zu erreichen.Probleme von Compiler generiert Assembly für intrinsics
den folgenden Code
#include <cmath>
#include <immintrin.h>
auto std_fma(float x, float y, float z)
{
return std::fma(x, y, z);
}
float _fma(float x, float y, float z)
{
_mm_store_ss(&x,
_mm_fmadd_ss(_mm_load_ss(&x), _mm_load_ss(&y), _mm_load_ss(&z))
);
return x;
}
float _sqrt(float x)
{
_mm_store_ss(&x,
_mm_sqrt_ss(_mm_load_ss(&x))
);
return x;
}
das Klirren 3.9 generierte Assembly mit -march = x86-64 Bei -mfma O3
std_fma(float, float, float): # @std_fma(float, float, float)
vfmadd213ss xmm0, xmm1, xmm2
ret
_fma(float, float, float): # @_fma(float, float, float)
vxorps xmm3, xmm3, xmm3
vmovss xmm0, xmm3, xmm0 # xmm0 = xmm0[0],xmm3[1,2,3]
vmovss xmm1, xmm3, xmm1 # xmm1 = xmm1[0],xmm3[1,2,3]
vmovss xmm2, xmm3, xmm2 # xmm2 = xmm2[0],xmm3[1,2,3]
vfmadd213ss xmm0, xmm1, xmm2
ret
_sqrt(float): # @_sqrt(float)
vsqrtss xmm0, xmm0, xmm0
ret
während der generierte Code für _sqrt
in Ordnung ist, gibt sind unnötig vxorps
(die das absolut unbenutzte xmm3 Register auf Null setzt) und movss
Anweisungen in _fma
im Vergleich zu std_fma
(die r ely auf Compiler intrinsische std :: fma)
der GCC 6.2 generierte Assembly mit -march = x86-64 -mfma O3
std_fma(float, float, float):
vfmadd132ss xmm0, xmm2, xmm1
ret
_fma(float, float, float):
vinsertps xmm1, xmm1, xmm1, 0xe
vinsertps xmm2, xmm2, xmm2, 0xe
vinsertps xmm0, xmm0, xmm0, 0xe
vfmadd132ss xmm0, xmm2, xmm1
ret
_sqrt(float):
vinsertps xmm0, xmm0, xmm0, 0xe
vsqrtss xmm0, xmm0, xmm0
ret
und hier sind eine Menge unnötiger vinsertps
Anweisungen
Arbeitsbeispiel: https://godbolt.org/g/q1BQym
Der Standard-x64 Aufruf Pass convention-Floating-Point-Funktionsargumente in XMM regi sters, so dass diese vmovss
und vinsertps
Anweisungen sollten beseitigt werden. Warum senden die genannten Compiler sie noch? Ist es möglich, sie ohne Inline-Montage loszuwerden?
Ich habe auch versucht, _mm_cvtss_f32
anstelle von _mm_store_ss
und mehrere Aufrufkonventionen zu verwenden, aber nichts geändert.
Das Ergebnis des intrinsischen '_mm_load_ss' ist ein 128-Bit-Vektor mit dem 32-Bit Gleitkommawert im ersten Element und Nullen in den anderen drei Elementen. Das tun die unnötigen Befehle und setzen die anderen drei Elemente auf Null. Die Compiler sind nicht schlau genug, um zu erkennen, dass diese Elemente nie verwendet und letztendlich verworfen werden, wenn die Funktion zurückkehrt, aber sie tun, was Sie von ihr verlangt haben. Es scheint, dass Sie bereits die perfekte Lösung für den FMA-Fall haben. –
Das ist wirklich schlecht, die Compiler sollten das wissen, da ich '* _ss' intrinsics verwende. – plasmacel
AFAIK, ist die einzige Lösung, das nicht zu tun (und ich denke, das ist ein Duplikat von http://StackOverflow.com/questions/39318496/How-to-merge-a-Scalar-into-Avector-without- the-Compiler-Verschwendung-eine-Anweisung). Clang sieht in einigen Fällen, dass die oberen Elemente unbenutzt sind und vermeiden können, sie zu berühren (siehe diese verbundene Frage). Sie können den Compiler dazu bringen, FMA zu verwenden, wenn dies für skalaren Code mit einer Option (nicht nur '-mfma' oder' -ffast-math') möglich ist, aber ich vergesse, was und habe keine Zeit, es jetzt zu suchen. Da 'std :: fma' Inlines perfekt ist, benutze es einfach. –