Ich möchte SIMD minmag and maxmag functions implementieren. Soweit ich verstehe, sind diese FunktionenSIMD minmag und maxmag
minmag(a,b) = |a|<|b| ? a : b
maxmag(a,b) = |a|>|b| ? a : b
Ich möchte diese für Float und Double und meine Zielhardware ist Haswell. Was ich wirklich brauche, ist Code, der beides berechnet. Hier ist, was ich für SSE4.1- für Doppel (der AVX-Code ist fast identisch)
static inline void maxminmag(__m128d & a, __m128d & b) {
__m128d mask = _mm_castsi128_pd(_mm_setr_epi32(-1,0x7FFFFFFF,-1,0x7FFFFFFF));
__m128d aa = _mm_and_pd(a,mask);
__m128d ab = _mm_and_pd(b,mask);
__m128d cmp = _mm_cmple_pd(ab,aa);
__m128d cmpi = _mm_xor_pd(cmp, _mm_castsi128_pd(_mm_set1_epi32(-1)));
__m128d minmag = _mm_blendv_pd(a, b, cmp);
__m128d maxmag = _mm_blendv_pd(a, b, cmpi);
a = maxmag, b = minmag;
}
Dies ist jedoch nicht so effizient ist wie Ich mag würde. Gibt es eine bessere Methode oder zumindest eine denkwürdige Alternative? Ich möchte versuchen, Port 1 zu vermeiden, da ich bereits viele Additionen/Subtraktionen über diesen Port habe. Die _mm_cmple_pd
Instrinsic geht an Port 1.
Die Hauptfunktion ich interessiert bin, ist dies:
//given |a| > |b|
static inline doubledouble4 quick_two_sum(const double4 & a, const double4 & b) {
double4 s = a + b;
double4 e = b - (s - a);
return (doubledouble4){s, e};
}
Also, was ich wirklich bin nach ist diese
static inline doubledouble4 two_sum_MinMax(const double4 & a, const double4 & b) {
maxminmag(a,b);
return quick_to_sum(a,b);
}
Edit: Mein Ziel ist es für two_sum_MinMax
schneller sein als two_sum
unter:
static inline doubledouble4 two_sum(const double4 &a, const double4 &b) {
double4 s = a + b;
double4 v = s - a;
double4 e = (a - (s - v)) + (b - v);
return (doubledouble4){s, e};
}
Edit: Hier ist die ultimative Funktion, nach der ich suche. Es fügt 20 Add/Subs hinzu, die alle an Port 1 von Haswell gehen. Wenn ich meine Implementierung von two_sum_MinMax
in dieser Frage verwende, wird sie auf 16 Add/Subs auf Port 1 heruntergesetzt, aber sie hat eine schlechtere Latenz und ist noch langsamer. Sie können die Baugruppe für diese Funktion sehen und erfahren Sie mehr darüber, warum ich über diese Pflege zu optimize-for-fast-multiplication-but-slow-addition-fma-and-doubledouble
static inline doublefloat4 adddd(const doubledouble4 &a, const doubledouble4 &b) {
doubledouble4 s, t;
s = two_sum(a.hi, b.hi);
t = two_sum(a.lo, b.lo);
s.lo += t.hi;
s = quick_two_sum(s.hi, s.lo);
s.lo += t.lo;
s = quick_two_sum(s.hi, s.lo);
return s;
// 2*two_sum, 2 add, 2*quick_two_sum = 2*6 + 2 + 2*3 = 20 add
}
mich korrigieren, wenn ich falsch, aber würde nicht 'minmag = blendv (a, b, cmp); maxmag = blendv (b, a, cmp); "tust du dasselbe wie deinen Code, während du dieselbe Maske wieder verwendest? – EOF
@EOF, es ist 'maxmag = _mm_blendv_pd (a, b, cmpi);' vielleicht hätte ich es 'icmp' anstelle von' cmpi' nennen sollen. Das "i" für invertieren. –
Ja, mir ist klar. Aber du kannst * auch * die Mischung invertieren, indem du die Argumente umkehrst ... – EOF