仍在学习SIMD的技巧,我有一个问题:我有两个打包的8位寄存器,我想与_mm_maddubs_epi16
(pmaddubsw
)相乘以获得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指令。
答案 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)
移动v1
或v2
,然后使用_mm_mullo_epi16()
。
可能的XY问题?我的猜测是_mm_unpacklo_epi8()
和_mm_packus_epi16()
对您可能有用。