【问题标题】:Multiply-subtract in SSESSE中的乘减法
【发布时间】:2015-09-05 17:50:01
【问题描述】:

我正在对一段代码进行矢量化处理,在某些时候我有以下设置:

register m128 a = { 99,99,99,99,99,99,99,99 }
register m128 b = { 100,50,119,30,99,40,50,20 }

我目前在这些寄存器中打包shorts,这就是为什么我每个寄存器有 8 个值。如果b 的第i 个值大于或等于a 中的值(在此在这种情况下,a 用常数 99 填充)。为此,我首先在ba 之间使用大于或等于运算,这会产生,例如:

register m128 c = { 1,0,1,0,1,0,0,0 }

为了完成运算,我想使用乘减法,即将运算b -= a*c 存储在b 中。结果将是:

b = { 1,50,20,30,0,40,50,20 }

有没有这样的操作?我发现的是 Haswell 的融合操作,但我目前正在研究 Sandy-Bridge。另外,如果有人对此有更好的想法,请告诉我(例如,我可以做一个逻辑减法:如果c 中的 1 则我减去,否则没有。

【问题讨论】:

  • 澄清一下,你的操作应该是b -= a*c,进行适当的减法吗?

标签: c vectorization sse


【解决方案1】:

您可以将b 复制到c,从c 中减去a,在16 位值中执行算术右移15 个位置,补足c 的值,使用掩码c a,最后从b中减去c

我不熟悉内在语法,但步骤是:

register m128 c = b;
c -= a;
c >>= 15;
c = ~c;
c &= a;
b -= c;

这里有一个步骤更少的替代方案:

register m128 c = compare_ge(b, a);
c = -c;
c &= a;
b -= c;

【讨论】:

    【解决方案2】:

    您基本上想要此代码的 SSE 版本,对吗?

    if (b >= a)
        t = b-a
    else
        t = b
    b = t
    

    因为我们想避免 SSE 版本的条件,所以我们可以像这样摆脱控制流(注意掩码是反转的):

    uint16_t mask = (b>=a)-1
    uint16_t tmp = b-a;
    uint16_t d = (b & mask) | (tmp & ~mask)
    b = d
    

    我检查了 _mm_cmpgt_epi16 内在函数,它有一个很好的属性,即它返回 0x0000 为 false 或 0xFFFF 为 true,而不是单个位 0 或 1(从而消除了对第一个减法)。因此我们的 SSE 版本可能如下所示。

    __m128i mask = _mm_cmpgt_epi16 (b, a)
    __m128i tmp = _mm_sub_epi16 (b, a)
    __m128 d = _mm_or_ps (_mm_and_ps (mask, tmp), _mm_andnot_ps (mask, b))
    

    编辑:哈罗德提到了一个简单得多的答案。如果您需要修改 if/elseelse 部分,上述解决方案可能会有所帮助。

    uint16_t mask = ~( (b>=a)-1 )
    uint16_t tmp = a & mask
    b = b - tmp
    

    SSE 代码将是

    __m128i mask = _mm_cmpgt_epi16 (b, a)
    __m128i t = _mm_sub_epi16 (b, _mm_and_si128 (mask, a))
    

    【讨论】:

    • 除了你使用d 我有c,是的。在我的情况下不需要您的操作b = d,因为我可以进行异地操作。我想你不想做m = (b>=a)-1m = (b>=a),对吧?
    • 没有。你的c 是我的m(用于面具)。 d 变量只是为了使其更具可读性。此外,m = (b>=a)-1 是正确的,因为我们将单个位掩码(0 或 1)转换为下一行所需的 8 位掩码(0000000 或 11111111)。
    • 为什么这么复杂?您不必混合减去和未减去的东西,您只需掩盖减数并从所有未受影响的元素中减去零。
    • 不是那样,只使用按位与,mul-high 在这里没有多大意义
    • 我刚刚注意到我为澄清一些事情而提出的修改被拒绝了。 stackoverflow.com/review/suggested-edits/8664046。我忘记了审稿人可能会在你开始之前看到建议的编辑。无论如何,` ~( (b>=a)-1 )` 非常复杂且难以阅读。 b>=a ? 0xFFFF : 0 会容易得多。另外,使用si128 逻辑,而不是ps
    【解决方案3】:

    另一种选择,如果你的输入是无符号的,你可以计算

    b = min(b, b-a);
    

    这是可行的,因为如果a>bb-a 环绕并保证产生比b 更大的值。对于a<=b,您将始终获得介于0b 之间的值。

    b = _mm_min_epu16(b, _mm_sub_epi16(b,a));
    

    所需的_mm_min_epu16 需要 SSE4.1 或更高版本(_mm_min_epu8 只需要 SSE2)。

    【讨论】:

      猜你喜欢
      • 2012-01-01
      • 2016-10-31
      • 2017-03-11
      • 2014-02-25
      • 1970-01-01
      • 2021-03-25
      • 2012-05-17
      • 2013-07-25
      相关资源
      最近更新 更多