将字节乘以产生16位,而不会移位

时间:2019-01-11 18:53:39

标签: sse simd avx

仍在学习SIMD的技巧,我有一个问题:我有两个打包的8位寄存器,我想与_mm_maddubs_epi16pmaddubsw)相乘以获得16-位打包寄存器。

我知道这些字节将总是产生 个小于256的数字,因此我想避免浪费剩余的8位。例如,_mm_maddubs_epi16(v1, v2)的结果应将结果写在r所在的XX中,而不要写在__所在的位置。

v1  (04, 00, 0e, 00, 04, 00, 04, 00, 0a, 00, 0f, 00, 05, 00, 01, 00)
v2  (04, 00, 0e, 00, 04, 00, 04, 00, 0a, 00, 0f, 00, 05, 00, 01, 00)

r   (__, XX, __, XX, __, XX, __, XX, __, XX, __, XX, __, XX, __, XX)

我可以不更改结果吗?

PS 。我没有一个好的处理器,只能使用AVX指令。

2 个答案:

答案 0 :(得分:3)

在向量图中,最高的元素在左侧还是右侧?是XX的最高或最低有效字节中的pmaddubsw位置?

要从每个单词高字节的输入中获取单词低字节的结果:

使用_mm_mulhi_epu16可以有效地完成(v1 << 8) * (v2 << 8) >> 16,并在与输入单词相反的字节中产生结果。由于您说乘积严格小于256 ,您将在每个16位字的低字节中得到8位结果。

(如果输入是带符号的,请使用_mm_mulhi_epi16,但是负的结果将被符号扩展为完整的16位。)

要从低字节的输入中获取单词高字节的结果

您需要更改加载/创建输入之一的方式,而不是

         MSB LSB | MSB LSB
v1_lo   (00, 04,   00, 0e, 00, 04, 00, 04, 00, 0a, 00, 0f, 00, 05, 00, 01)
 element# 15 14   13   12 ...                                           0

您有以下内容:(这两种方法都使用Intel的符号,其中左元素是最高编号,因此向量向_mm_slli_epi128的方向向左移动字节)。

         MSB LSB | MSB LSB 
v1_hi   (04, 00,   0e, 00, 04, 00, 04, 00, 0a, 00, 0f, 00, 05, 00, 01, 00)
 element# 15 14   13   12 ...                                           0

在每个单词元素的高半部分中,v2仍具有其非零字节,只需_mm_mullo_epi16(v1_hi, v2) ,您将获得(v1 * v2) << 8免费。

如果您已经用0拆包字节以获得v1和v2,则用另一种方式拆包。如果您使用的是pmovzx_mm_cvtepu8_epi16),请切换到使用_mm_unpacklo_epi8(_mm_setzero_si128(), packed_v1 )

如果要以这种已经零填充的形式从内存中加载这些向量,请使用未对齐的加载偏移量1个字节,以便零最终位于相反的位置。


如果您真正想要的是从没有以零解包的输入字节开始,那么我认为您不能避免这种情况。或者,如果要掩盖而不是解包(改为使用_mm_and_si128来节省shuffle-port吞吐量),则可能需要在某个地方进行转移。不过,您可以使用v1_hi = _mm_slli_epi16(v, 8)来使掩盖的代替一种方式:用字粒度向左移动8会把低字节清零。

答案 1 :(得分:0)

移动v1v2,然后使用_mm_mullo_epi16()

可能的XY问题?我的猜测是_mm_unpacklo_epi8()_mm_packus_epi16()对您可能有用。