assembly - SIMD minmag 和 maxmag

标签 assembly floating-point x86 sse avx

我想实现 SIMD minmag and maxmag functions .据我了解这些功能是

minmag(a,b) = |a|<|b| ? a : b
maxmag(a,b) = |a|>|b| ? a : b

我想要这些用于 float 和 double 并且我的目标硬件是 Haswell。我真正需要的是计算两者的代码。这是我对 SSE4.1 的 double(AVX 代码几乎相同)
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;
}

但是,这并不像我希望的那样有效。 是否有更好的方法或至少值得考虑的替代方案? 我想尽量避免使用端口 1,因为我已经使用该端口进行了许多加法/减法。 _mm_cmple_pd内在进入端口 1。

我感兴趣的主要功能是:
//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};
}

所以我真正追求的是这个
static inline doubledouble4 two_sum_MinMax(const double4 & a, const double4 & b) {
    maxminmag(a,b);       
    return quick_to_sum(a,b);
}

编辑:我的目标是 two_sum_MinMaxtwo_sum 快以下:
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};
}

编辑:这是我追求的终极功能。它执行 20 个添加/订阅,所有这些都转到 Haswell 上的端口 1。使用我对 two_sum_MinMax 的实现在这个问题中,它在端口 1 上减少到 16 个添加/订阅,但它具有更差的延迟并且仍然更慢。您可以在 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
}

最佳答案

这是使用较少指令的替代实现:

static inline void maxminmag_test(__m128d & a, __m128d & b) {
    __m128d cmp     = _mm_add_pd(a, b); // test for mean(a, b) >= 0
    __m128d amin    = _mm_min_pd(a, b);
    __m128d amax    = _mm_max_pd(a, b);
    __m128d minmag  = _mm_blendv_pd(amin, amax, cmp);
    __m128d maxmag  = _mm_blendv_pd(amax, amin, cmp);
    a = maxmag, b = minmag;
}

它使用了一个有点微妙的算法(见下文),结合我们可以使用符号位作为选择掩码的事实。

它还使用了@EOF 的建议,即只使用一个掩码并切换操作数顺序,从而节省了一条指令。

我已经用少量案例对其进行了测试,它似乎与您的原始实现相匹配。

算法:
 if (mean(a, b) >= 0)       // this can just be reduced to (a + b) >= 0
 {
     minmag = min(a, b);
     maxmag = max(a, b);
 }
 else
 {
     minmag = max(a, b);
     maxmag = min(a, b);
 }

关于assembly - SIMD minmag 和 maxmag,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30618991/

相关文章:

floating-point - 我如何证明 f64::from_bits(0x3fe9000000000000 u64) == 0.781250 f64

floating-point - 128 位 "long-float"有用吗?

performance - 非时间负载和硬件预取器,它们可以一起工作吗?

assembly - RaspberryPI/BCM2835 内存布局

c - 如何从 C 代码中获取单操作数 imul

assembly - x86 程序集 : Segmentation Fault (Core dumped) while trying to reverse print array

assembly - 将一个字节压入堆栈(IA32)

c - 如何为ARMCC声明全局浮点寄存器

delphi - Formatfloat 是如何工作的

c++ - 我的汇编函数推送数据两次