c - SSE 中的乘减法

标签 c vectorization sse

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

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

我目前正在这些寄存器中打包 short,这就是为什么每个寄存器有 8 个值。我想做的是如果 b 的第 i 个值减去 b 中的第 i 个元素与 a 中的相应值大于或等于 a 中的值(在这种情况下,a 填充为常量 99 )。为此,我首先在 ba 之间使用大于或等于操作,对于这个例子,它会产生:

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

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

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

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

最佳答案

您本质上想要此代码的 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))

关于c - SSE 中的乘减法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30943332/

相关文章:

c - 错误 : passing argument 1 of ‘kthread_create_on_node’ from incompatible pointer type

performance - 以向量化方式从非标量结构体数组中的元素减去常量值

c - 为什么这个 SIMD 示例代码可以使用 minGW 进行 C 编译,但可执行文件无法在我的 Windows 计算机上运行?

iphone - iPhone 支持 SSE2 吗?

c - 有什么办法可以使这个添加无限?

c++ - 覆盖 C 代码以抛出 C++ 异常?

c - BullsEye代码覆盖率

r - 向量化这个 for 循环(当前行依赖于上面的行)

visual-studio - 如何在 Visual Studio 2015(对于 C++)中仅禁用 SIMD 自动矢量化优化?

sse - 如何对 SSE XMM、AVX YMM 和 ZMM 寄存器中的所有 32 位或 64 位子寄存器求和?