新AVX512指令的成本 - 分散存储

时间:2017-09-04 18:23:42

标签: performance x86 intrinsics avx512

我正在使用新的AVX512指令集,我试着了解它们的工作原理以及如何使用它们。

我尝试的是交错由掩码选择的特定数据。 我的小基准测试将x * 32字节的对齐数据从内存加载到两个向量寄存器中,并使用动态掩码压缩它们(图1)。得到的向量寄存器被分散到存储器中,因此两个向量寄存器是交错的(图2)。

Compression of the two vector register

图1:使用相同的动态创建的掩码压缩两个数据向量寄存器。

Scatter store to interleave

图2:用于交错压缩数据的分散存储。

我的代码如下所示:

void zipThem( uint32_t const * const data, __mmask16 const maskCompress, __m512i const vindex, uint32_t * const result ) {
   /* Initialize a vector register containing zeroes to get the store mask */
   __m512i zeroVec     = _mm512_setzero_epi32();
   /* Load data */
   __m512i dataVec_1   = _mm512_conflict_epi32( data );
   __m512i dataVec_2   = _mm512_conflict_epi32( data + 16 );
   /* Compress the data */
   __m512i compVec_1   = _mm512_maskz_compress_epi32( maskCompress, dataVec_1 );
   __m512i compVec_2   = _mm512_maskz_compress_epi32( maskCompress, dataVec_2 );

   /* Get the store mask by compare the compressed register with the zero-register (4 means !=) */
   __mmask16 maskStore = _mm512_cmp_epi32_mask( zeroVec, compVec_1, 4 );

   /* Interleave the selected data */
   _mm512_mask_i32scatter_epi32(
      result,
      maskStore,
      vindex,
      compVec_1,
      1
   );
   _mm512_mask_i32scatter_epi32(
      result + 1,
      maskStore,
      vindex,
      compVec_2,
      1
   );
}

我用

编译了所有内容
  

-O3 -march = knl -lmemkind -mavx512f -mavx512pf

我称该方法为100'000'000元素。为了实际了解散点存储的行为,我使用maskCompress的不同值重复此测量。 我期望执行所需的时间与maskCompress中的设置位数之间存在某种依赖关系。但我观察到,测试需要大致相同的执行时间。以下是性能测试的结果: Results of the measurement 图3:测量结果。 x轴表示写入元素的数量,具体取决于maskCompressed。 y轴显示性能。

可以看出,当实际将更多数据写入内存时,性能会越来越高。

我做了一些研究,并提出了这个问题:Instruction latency of avx512。在给定链接之后,所使用指令的延迟是恒定的。但说实话,我对这种行为有点困惑。

关于克里斯托夫和彼得的答案,我改变了我的方法。因此我不知道如何使用unpackhi / unpacklo来交错稀疏矢量寄存器,我只是将AVX512压缩内在函数与shuffle(vpermi)结合起来:

int zip_store_vpermit_cnt(
  uint32_t const * const data, 
  int const compressMask, 
  uint32_t * const result,
  std::ofstream & log   
  ) {
  __m512i data1 = _mm512_undefined_epi32();
  __m512i data2 = _mm512_undefined_epi32();
  __m512i comp_vec1 = _mm512_undefined_epi32();
  __m512i comp_vec2 = _mm512_undefined_epi32();
  __mmask16 comp_mask = compressMask;
  __mmask16 shuffle_mask;
  uint32_t store_mask = 0;
  __m512i shuffle_idx_lo = _mm512_set_epi32(
    23, 7, 22, 6, 
    21, 5, 20, 4,
    19, 3, 18, 2,
    17, 1, 16, 0 );
  __m512i shuffle_idx_hi = _mm512_set_epi32(
    31, 15, 30, 14,
    29, 13, 28, 12,
    27, 11, 26, 10,
    25, 9, 24, 8 );
  std::size_t pos = 0;
  int pcount = 0;
  int fullVec = 0;
  for( std::size_t i = 0; i < ELEM_COUNT; i += 32 ) {
    /* Loading the current data */
    data1 = _mm512_maskz_compress_epi32( comp_mask, _mm512_load_epi32( &(data[i]) ) );
    data2 = _mm512_maskz_compress_epi32( comp_mask, _mm512_load_epi32( &(data[i+16]) ) );
    shuffle_mask = _mm512_cmp_epi32_mask( zero, data2, 4 );
    /* Interleaving the two vector register, depending on the compressMask */
    pcount = 2*( __builtin_popcount( comp_mask ) );
    store_mask = std::pow( 2, (pcount) ) - 1;
    fullVec = pcount / 17;
    comp_vec1 = _mm512_permutex2var_epi32( data1, shuffle_idx_lo, data2 );
    _mm512_mask_storeu_epi32( &(result[pos]), store_mask, comp_vec1 );
    pos += (fullVec) * 16 + ( ( 1 - ( fullVec ) ) * pcount ); // same as pos += ( pCount >= 16 ) ? 16 : pCount;
    _mm512_mask_storeu_epi32( &(result[pos]), (store_mask >> 16) , comp_vec2 );
    pos += ( fullVec ) * ( pcount - 16 );                     // same as pos += ( pCount >= 16 ) ? pCount - 16 : 0;

    //a simple _mm512_store_epi32 produces a segfault, because the memory isn't aligned anymore :(

  }
  return pos;
}

这样,两个向量寄存器内的稀疏数据可以交错。不幸的是,我必须手动计算商店的面具。这似乎相当昂贵。可以使用LUT来避免计算,但我认为这不是应该的方式。

Performance of storing 图4:4种不同商店的性能测试结果。

我知道这不是通常的方法,但我有3个问题,与此主题相关,我希望有人可以帮助我。

  1. 为什么只有一个设置位的屏蔽存储需要与屏蔽存储相同的时间才能设置所有位?

  2. 是否有人有经验或有良好的文档来了解AVX512分散存储的行为?

  3. 是否有更容易或更高效的方法来交错两个向量寄存器?

  4. 感谢您的帮助!

    此致

0 个答案:

没有答案