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;
}
}
其中row和cols都是2的倍数。我称“stride”为从一个像素到图像中紧邻下方的像素的字节步长。
现在我想要对此进行矢量化。这个想法是:
a
中的16个字节,并在b
a
和b
之间的最小字节数。存储在a
。a
的副本,将其右移1个字节(8位)。将其存储在b
。a
和b
之间的最小字节数。存储在a
。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
类型,并且与我想要使用的移位内在函数不兼容。在C ++中,我用this templated data structure解决了问题,而我被告知问题cannot be reliably addressed using union (编辑:虽然答案是指C ++,实际上是in C type punning IS allowed),也不是using pointers to cast,因为这会破坏严格的别名规则。
使用ARM Neon内在函数时,组合不同数据类型的方法是什么?
答案 0 :(得分:2)
对于这类问题,arm_neon.h提供vreinterpret{q}_dsttype_srctype转换操作符。
在某些情况下,您可能希望将矢量视为具有 不同类型,不改变其价值。一组内在函数是 提供执行此类转换。
因此,假设a
和b
被声明为:
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);