1

根据文档,从gcc 4.9指令AVX-512集开始支持,但我有gcc 4.8. 我目前有这样的代码来总结一块内存(保证小于 256 字节,所以不用担心溢出):

__mm128i sum = _mm_add_epi16(sum, _mm_cvtepu8_epi16(*(__m128i *) &mem));

现在,查看文档,如果我们有剩余的四个字节,我可以使用:

__mm128i sum = _mm_add_epi16(sum,
                             _mm_mask_cvtepu8_epi16(_mm_set1_epi16(0),
                                                    (__mmask8)_mm_set_epi16(0,0,0,0,1,1,1,1),
                                                    *(__m128i *) &mem));

(注意,__mmask8我能找到的任何地方似乎都没有记录类型,所以我猜......)

但是,_mm_mask_cvtepu8_epi16是一条AVX-512指令,那么有没有办法复制它?我试过了:

mm_mullo_epi16(_mm_set_epi16(0,0,0,0,1,1,1,1),
               _mm_cvtepu8_epi16(*(__m128i *) &mem));

但是,存在缓存停顿,因此直接for (int i = 0; i < remaining_bytes; i++) sum += mem[i];提供更好的性能。

4

1 回答 1

2

当我碰巧偶然发现这个问题时,它仍然没有得到答案,如果这仍然是一个问题......

对于您的示例问题,您走在正确的轨道上。

  • 乘法是一个相对较慢的操作,所以你应该避免使用_mm_mullo_epi16. 改为使用_mm_and_si128按位 AND 是一种更快的操作,例如_mm_and_si128(_mm_cvtepu8_epi16(*(__m128i *) &mem), _mm_set_epi32(0, 0, -1, -1))
  • 我不确定缓存停顿是什么意思,但是如果内存访问是瓶颈,并且编译器不会将上述常量放入寄存器中,则可以使用_mm_srli_si128(vector, 8)不需要任何额外寄存器的东西/内存加载。移位可能比 AND 慢。
  • 如果它总是 8 个字节,你可以使用_mm_move_epi64
  • 如果剩余数量不是固定数量的元素(例如,您有n%16一些任意的字节n),这些都不能解决这种情况。 请注意,AVX-512 也不能真正解决它。 如果您需要处理这种情况,您可以有一个掩码表,并且取决于剩余的内容,例如_mm_and_si128(vector, masks[n & 0xf])
  • _mm_mask_cvtepu8_epi16只关心向量的下半部分,所以你的例子有点令人困惑——也就是说,你不需要屏蔽任何东西,因为后面的元素被完全忽略了)

在更通用的级别上,掩码操作实际上只是嵌入式_mm_blend_epi16(或等效)。对于归零习语,它们可以很容易地用_mm_and_si128/来模拟_mm_andnot_si128,如上所示。

于 2017-09-10T09:42:42.277 回答