将ARM NEON中每个字节的高位打包,像AVX512 vpmovb2m一样为64字节?

问题描述 投票:0回答:1

__builtin_ia32_cvtb2mask512()
GNU C 内置,用于
vpmovb2m k, zmm

它的英特尔内在是
_mm512_movepi8_mask

它从每个字节中提取最高有效位,生成一个整数掩码。

SSE2 和 AVX2 指令

pmovmskb
vpmovmskb
对 16 或 32 字节向量执行相同的操作,在 GPR 而不是 AVX-512 掩码寄存器中生成掩码。 (
_mm_movemask_epi8
_mm256_movemask_epi8
)。

  1. 我想要一个比下面更快的 ARM 实现
  2. 我想要 ARM NEON 的实现
  3. 我想要 ARM SVE 的实现

我已经附上了 C 语言的基本标量实现。对于那些尝试在 ARM 中实现此功能的人,我们关心高位,但每个字节的高位(在 128 位向量中)可以使用 ARM 轻松移位到低位NEON 内在函数:vshrq_n_u8()。 请注意,我不想将位图存储到内存中,它应该只是类似于以下函数的函数的返回值。

#define _(n) __attribute((vector_size(1<<n),aligned(1)))
typedef char V  _(6); // 64 bytes, 512 bits
typedef unsigned long U;
#undef _
U generic_cvtb2mask512(V v) {
   U mask=0;int i=0; 
   while(i<64){
     // shift mask by 1 and OR with MSB of v[i] byte
     mask=(mask<<1)|((v[i]&0x80)>>7);
     i++;}
   return mask;
}

这是 16 字节(128b 向量)的一种可能算法,只需将其放入 64 字节(512b 向量)的循环中:

#define _(n) __attribute((vector_size(1<<n),aligned(1)))
typedef char g4 _(4); // 16 bytes, 128 bits
typedef char g3 _(3); // 8 bytes,   64 bits
typedef unsigned long U;
#undef _

unsigned short get_16msb(g4 v) {
  unsigned short = ret;

  // per byte, make every bit same as msb
  g4 msb = vdupq_n_u8(0x80);
  g4 filled = vceqq_u8(v, msb);

  // create a mask of each bit value
  g4 b = {0x80, 0x40, 0x20, 0x01, 0x08, 0x04, 0x02, 0x01,
          0x80, 0x40, 0x20, 0x01, 0x08, 0x04, 0x02, 0x01};

  // and vectors together
  g4 z = vandq_u8 (filled,b);

  // extract lower 8 bytes, hi 8 bytes
  g3 lo = vget_low_u8(z);
  g3 hi = vget_high_u8(z);

  // and the 8 bytes of lo together ...
  // put in byte 1 of ret
  // and the 8 bytes of hi together ...  
  // put in byte 2 of ret

  return ret;
}
c arm simd arm64 neon
1个回答
0
投票

当大多数/最好的优化都是针对特定的时,想要优化通用的就会很困难。尤其是您想对结果做什么。

例如“检查是否设置了任何高位”的代码可能比“检查设置了哪个高位”便宜得多。

  // per byte, make every bit same as msb
  g4 msb = vdupq_n_u8(0x80);
  g4 filled = vceqq_u8(v, msb);

不会对性能产生影响,但它会检查符号位是否已设置,所以只需执行

vcltzq_s8(v)
即可。即,代替
v == 0x80
,只需检查有符号比较中的值是否为负。

如果您只关心是否存在设置有符号位的值,对于 Adv SIMD,您可以在比较结果上使用

vpmaxq_s8
并执行以下操作:

if (vgetq_lane_s64 (vreinterpretq_s64_s8 (res), 0))

对于 SVE,您不需要这个,因为比较本身会设置标志。您可以对比较的谓词结果执行

ptest
并对其进行分支。编译器应该能够在优化期间删除
ptest

如果您需要使用哪个元素,有多种方法。 正如 Peter Cordes 在评论中所说,您可以使用带有特殊掩码的 AND 和用于 Adv 的

clz
。 SIMD。

这些模式很常见,本质上是来自标准库的

strchr
。 因此,为了获得最佳序列,我建议检查 Arm 优化例程中的所有内容,当我们找到更好的方法时,我们会不断更新这些例程。

对于 Neon:https://github.com/ARM-software/optimized-routines/blob/master/string/aarch64/strchr.S 是文件,其操作如上所述。

对于SVE:https://github.com/ARM-software/optimized-routines/blob/master/string/aarch64/strchr-sve.S那里有一些额外的代码,因为

strchr
需要检查null终结者,但总体思路是一样的。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.