字节相乘产生 16 位,无需移位
Multiply bytes to produce 16-bits, without shifting
仍在学习 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 指令。
Shift v1
或 v2
然后使用 _mm_mullo_epi16()
.
可能是 XY 问题?我的猜测是 _mm_unpacklo_epi8()
和 _mm_packus_epi16()
可能对您有用。
在你的矢量图中,最高的元素是在左边还是右边? 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
免费。
如果您已经用零解包字节以获得 v1 和 v2,则以另一种方式解包。如果您使用 pmovzx
(_mm_cvtepu8_epi16
),则切换到使用 _mm_unpacklo_epi8(_mm_setzero_si128(), packed_v1 )
.
如果您以这种已经用零填充的形式从内存中加载这些向量,请使用 1 个字节的未对齐加载偏移,以便零最终位于相反的位置。
如果您真正想要的是从未用零解包的输入字节开始,我认为您无法避免这种情况。或者,如果您正在屏蔽而不是解包(通过使用 _mm_and_si128
来节省洗牌端口吞吐量),您可能需要在某个地方换班。但是,您可以使用 v1_hi = _mm_slli_epi16(v, 8)
以一种方式移动 而不是 屏蔽:左移 8 字粒度将使低字节归零。
仍在学习 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 指令。
Shift v1
或 v2
然后使用 _mm_mullo_epi16()
.
可能是 XY 问题?我的猜测是 _mm_unpacklo_epi8()
和 _mm_packus_epi16()
可能对您有用。
在你的矢量图中,最高的元素是在左边还是右边? 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
免费。
如果您已经用零解包字节以获得 v1 和 v2,则以另一种方式解包。如果您使用 pmovzx
(_mm_cvtepu8_epi16
),则切换到使用 _mm_unpacklo_epi8(_mm_setzero_si128(), packed_v1 )
.
如果您以这种已经用零填充的形式从内存中加载这些向量,请使用 1 个字节的未对齐加载偏移,以便零最终位于相反的位置。
如果您真正想要的是从未用零解包的输入字节开始,我认为您无法避免这种情况。或者,如果您正在屏蔽而不是解包(通过使用 _mm_and_si128
来节省洗牌端口吞吐量),您可能需要在某个地方换班。但是,您可以使用 v1_hi = _mm_slli_epi16(v, 8)
以一种方式移动 而不是 屏蔽:左移 8 字粒度将使低字节归零。