【问题标题】:sequence of SSE instructions for a conditional expression条件表达式的 SSE 指令序列
【发布时间】:2022-01-17 00:02:53
【问题描述】:

至少有2种表达方式,

a ? b : c

假设 true 全部开启 (0xff...),false 全部关闭 (0)。

第一个是,

(a & b) | (~a & c)

第二个是,

(a & (b ^ c)) ^ c

SSE 指令的相关序列分别是,

andxx
andnxx
orxx

xorxx
andxx
xorxx

您更喜欢哪种方式,为什么?

【问题讨论】:

标签: c assembly x86-64 sse


【解决方案1】:

我最终都使用了。它们有不同的优势。

typedef __m128 p4f_t;

static inline p4f_t p4f_if(p4f_t a, p4f_t b, p4f_t c) {
    return p4f_or(p4f_and(a, b), p4f_andn(a, c));
}

static inline p4f_t p4f_if2(p4f_t a, p4f_t b, p4f_t c) {
    return p4f_xor(p4f_and(a, p4f_xor(b, c)), c);
}

我在最后发布了包装器代码。下面的negifnegif2 在做同样的工作时会产生不同的目标代码。

static inline p4f_t p4f_neg(p4f_t a) {
    p4f_t mask = p4f_fill_bitCopy(0x80000000);
    return p4f_xor(a, mask);
}

p4f_t negif(p4f_t a, p4f_t b) {
    return p4f_if(b, p4f_neg(a), a);
}

p4f_t negif2(p4f_t a, p4f_t b) {
    return p4f_if2(b, p4f_neg(a), a);
}

这是反汇编。

<negif>:
movaps xmm3,XMMWORD PTR .LC0[rip]
movaps xmm2,xmm1
andnps xmm2,xmm0
xorps  xmm3,xmm0
andps  xmm1,xmm3
orps   xmm1,xmm2
movaps xmm0,xmm1
ret

<negif2>:
andps  xmm1,XMMWORD PTR .LC0[rip]
xorps  xmm0,xmm1
ret

为什么negif2 优化得更好?

neg(a) = xor(a, mask)
if2(a, b, c) = xor(and(a, xor(b, c)), c)
negif2(a, b) = if2(b, neg(a), a)
             = xor(and(b, xor(neg(a), a)), a)
             = xor(and(b, xor(xor(a, mask), a)), a)
             = xor(and(b, mask), a)

negif2 可以通过简单的替换来简化,而negif 不能,因此至少当前版本的 gcc 无法正确优化 negif

然而,正如 @RaymondChen 和 @chtz 在 cmets 中提到的,andandn 在第一个 if 中没有相互依赖关系;它们可以并行运行,因此在其他情况下,p4f_if 应该是 p4f_if2 的选择。

这是包装代码。

static inline p4f_t p4f_fill(float a) {
    return _mm_set1_ps(a);
}

static inline p4f_t p4f_fill_bitCopy(uint32_t a) {
    float a_;
    memcpy(&a_, &a, sizeof(a));
    return p4f_fill(a_);
}

static inline p4f_t p4f_and(p4f_t a, p4f_t b) {
    return _mm_and_ps(a, b);
}

static inline p4f_t p4f_andn(p4f_t a, p4f_t b) {
    return _mm_andnot_ps(a, b);
}

static inline p4f_t p4f_or(p4f_t a, p4f_t b) {
    return _mm_or_ps(a, b);
}

static inline p4f_t p4f_xor(p4f_t a, p4f_t b) {
    return _mm_xor_ps(a, b);
}

【讨论】:

  • negif 中有趣的错过优化,GCC 和 clang 都错过了使用 and/andn 版本的简化:godbolt.org/z/Yhen46eK8。我一开始就不会那样写那个函数。我会做xor(a, and(b, signbit_mask))。使用p4f_if2(屏蔽异或)的唯一原因是您已经知道优化,因此您最好简化一个用例,而不是使用另一个版本的位混合函数。如果你在两者中都使用#ifdef __SSE4_1__ 来使用blendvps,那么你的优化就会消失。
猜你喜欢
  • 2020-09-18
  • 2011-11-08
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
  • 1970-01-01
  • 2015-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多