【问题标题】:ARM Neon in C: How to combine different 128bit data types while using intrinsics?C 中的 ARM Neon:如何在使用内部函数时组合不同的 128 位数据类型?
【发布时间】:2017-04-20 12:13:09
【问题描述】:

TLTR

对于 arm 内在函数,如何将 uint8x16_t 类型的 128 位变量输入到期望 uint16x8_t 的函数中?


扩展版

上下文:我有一个灰度图像,每像素 1 个字节。我想将其缩小 2 倍。对于每个 2x2 输入框,我想取最小像素。在纯 C 中,代码如下所示:

for (int y = 0; y < rows; y += 2) {
    uint8_t* p_out = outBuffer + (y / 2) * outStride;
    uint8_t* p_in = inBuffer + y * inStride;
    for (int x = 0; x < cols; x += 2) {
         *p_out = min(min(p_in[0],p_in[1]),min(p_in[inStride],p_in[inStride + 1]) );
         p_out++;
         p_in+=2;
    }
}

其中行和列都是 2 的倍数。我将“跨步”称为从图像中的一个像素到紧接下方的像素所需的字节步骤。

现在我想对它进行矢量化。这个想法是:

  1. 连续取 2 行像素
  2. 从顶行开始在a 中加载16 个字节,并在b 中立即加载下面的16 个字节
  3. 逐字节计算ab 之间的最小值。存储在a
  4. 创建a 的副本,将其右移1 个字节(8 位)。将其存储在b
  5. 逐字节计算ab 之间的最小值。存储在a
  6. 在输出图像中存储a 的每一秒字节(丢弃一半字节)

我想用 Neon 内部函数来写这个。好消息是,每一步都存在与之匹配的内在函数。

例如,在第 3 点可以使用(来自here):

uint8x16_t  vminq_u8(uint8x16_t a, uint8x16_t b);

在第 4 点,可以使用 8 位移位(来自 here)使用以下之一:

uint16x8_t vrshrq_n_u16(uint16x8_t a, __constrange(1,16) int b);
uint32x4_t vrshrq_n_u32(uint32x4_t a, __constrange(1,32) int b);
uint64x2_t vrshrq_n_u64(uint64x2_t a, __constrange(1,64) int b);

那是因为我不关心字节 1,3,5,7,9,11,13,15 会发生什么,因为无论如何它们都会从最终结果中被丢弃。 (这里的正确性已经过验证,不是问题的重点。)

但是,vminq_u8 的输出是 uint8x16_t 类型,它与我想使用的 shift 内在函数不兼容。在 C++ 中,我用this templated data structure 解决了这个问题,而我被告知问题cannot be reliably addressed using union (编辑:虽然那个答案是指C++,实际上是in C type punning IS allowed,也不是@ 987654326@,因为这会破坏严格的别名规则。

在使用 ARM Neon 内部函数时组合不同数据类型的方法是什么?

【问题讨论】:

    标签: c arm vectorization neon


    【解决方案1】:

    对于此类问题,arm_neon.h 提供了vreinterpret{q}_dsttype_srctype 转换运算符。

    在某些情况下,您可能希望将向量视为具有 不同的类型,而不改变它的值。一组内在函数是 提供来执行这种类型的转换。

    所以,假设 ab 被声明为:

    uint8x16_t a, b;
    

    你的第4点可以写成(*)

    b = vreinterpretq_u8_u16(vrshrq_n_u16(vreinterpretq_u16_u8(a), 8) );
    

    然而,请注意,不幸的是,这并不能解决使用向量类型数组的数据类型,请参阅ARM Neon: How to convert from uint8x16_t to uint8x8x2_t?


    (*) 应该说,这比等效(在此特定上下文中)SSE 代码要麻烦得多,因为 SSE 只有一种 128 位整数数据类型(即__m128i):

    __m128i b = _mm_srli_si128(a,1);
    

    【讨论】:

      猜你喜欢
      • 2012-06-30
      • 2020-05-28
      • 2013-09-18
      • 2019-09-03
      • 2012-02-22
      • 2022-06-17
      • 2011-02-06
      • 2013-05-08
      • 2012-08-07
      相关资源
      最近更新 更多