c - 如何执行_mm256_movemask_epi8 (VPMOVMSKB) 的反函数?

标签 c x86 simd avx avx2

内在的:

int mask = _mm256_movemask_epi8(__m256i s1)

创建一个掩码,其 32 位对应于 s1 每个字节的最高有效位。在使用位操作(例如 BMI2)操作掩码后,我想执行 _mm256_movemask_epi8 的逆操作,即创建一个 __m256i vector 每个字节的最高有效位包含 uint32_t 掩码的相应位。

执行此操作的最佳方法是什么?

编辑: 我需要执行逆运算,因为内在 _mm256_blendv_epi8 只接受 __m256i 类型掩码而不是 uint32_t。因此,在生成的 __m256i 掩码中,我可以忽略每个字节的 MSB 以外的位。

最佳答案

我已经在 Haswell 机器上实现了上述三种方法。 Evgeny Kluev 的方法最快(1.07 秒),其次是 Jason R 的方法(1.97 秒)和 Paul R 的方法(2.44 秒)。下面的代码是使用 -march=core-avx2 -O3 优化标志编译的。

#include <immintrin.h>
#include <boost/date_time/posix_time/posix_time.hpp>

//t_icc = 1.07 s
//t_g++ = 1.09 s
__m256i get_mask3(const uint32_t mask) {
  __m256i vmask(_mm256_set1_epi32(mask));
  const __m256i shuffle(_mm256_setr_epi64x(0x0000000000000000,
      0x0101010101010101, 0x0202020202020202, 0x0303030303030303));
  vmask = _mm256_shuffle_epi8(vmask, shuffle);
  const __m256i bit_mask(_mm256_set1_epi64x(0x7fbfdfeff7fbfdfe));
  vmask = _mm256_or_si256(vmask, bit_mask);
  return _mm256_cmpeq_epi8(vmask, _mm256_set1_epi64x(-1));
}

//t_icc = 1.97 s
//t_g++ = 1.97 s
__m256i get_mask2(const uint32_t mask) {
  __m256i vmask(_mm256_set1_epi32(mask));
  const __m256i shift(_mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0));
  vmask = _mm256_sllv_epi32(vmask, shift);
  const __m256i shuffle(_mm256_setr_epi64x(0x0105090d0004080c,
      0x03070b0f02060a0e, 0x0105090d0004080c, 0x03070b0f02060a0e));
  vmask = _mm256_shuffle_epi8(vmask, shuffle);
  const __m256i perm(_mm256_setr_epi64x(0x0000000000000004, 0x0000000100000005,
      0x0000000200000006, 0x0000000300000007));
  return _mm256_permutevar8x32_epi32(vmask, perm);
}

//t_icc = 2.44 s
//t_g++ = 2.45 s
__m256i get_mask1(uint32_t mask) {
  const uint64_t pmask = 0x8080808080808080ULL; // bit unpacking mask for PDEP
  uint64_t amask0, amask1, amask2, amask3; 
  amask0 = _pdep_u64(mask, pmask);
  mask >>= 8;
  amask1 = _pdep_u64(mask, pmask);
  mask >>= 8;
  amask2 = _pdep_u64(mask, pmask);
  mask >>= 8;
  amask3 = _pdep_u64(mask, pmask);
  return _mm256_set_epi64x(amask3, amask2, amask1, amask0);
}

int main() {
  __m256i mask;
  boost::posix_time::ptime start(
      boost::posix_time::microsec_clock::universal_time()); 
  for(unsigned i(0); i != 1000000000; ++i)
    { 
      mask = _mm256_xor_si256(mask, get_mask3(i));
    }
  boost::posix_time::ptime end(
      boost::posix_time::microsec_clock::universal_time());
  std::cout << "duration:" << (end-start) << 
    " mask:" << _mm256_movemask_epi8(mask) << std::endl;
  return 0;
}

关于c - 如何执行_mm256_movemask_epi8 (VPMOVMSKB) 的反函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21622212/

相关文章:

c++ - 为什么 GCC 不能向量化这个函数和循环?

x86 - 是否可以在不使用 SSE4 的情况下对 VC++ 中的乘法进行向量化?

c - Actor 模型库、框架或用 C 编写的语言?

c++ - 通过 CDC COM 端口到 Arduino 的 Windows COM 通信

c++ - 使用 GetLogicalProcessorInformation() 查询系统缓存信息的结果无效

assembly - MUL/DIV 指令与 MOV & SHL/SHR (Pentium Pro)

assembly - 该汇编语言代码是什么意思?

c - 将 POSIX C 代码移植到 Windows

c - 从 C 中的 FIFO 读取 : select() doesn't return?

c - 了解 SIMD 随机播放控制掩码