我使用SSE进行了矢量化内在函数的第一种方法,其中基本上只有一种数据类型__m128i
。切换到Neon我发现数据类型和函数原型更加具体,例如uint8x16_t
(向量为16 unsigned char
),uint8x8x2_t
(2个向量,每个unsigned char
为uint32x4_t
,uint32_t
(向量为4 struct uint_128bit_t { union {
uint8x16_t uint8x16;
uint16x8_t uint16x8;
uint32x4_t uint32x4;
uint8x8x2_t uint8x8x2;
uint8_t uint8_array[16] __attribute__ ((aligned (16) ));
uint16_t uint16_array[8] __attribute__ ((aligned (16) ));
uint32_t uint32_array[4] __attribute__ ((aligned (16) ));
};
operator uint8x16_t& () {return uint8x16;}
operator uint16x8_t& () {return uint16x8;}
operator uint32x4_t& () {return uint32x4;}
operator uint8x8x2_t& () {return uint8x8x2;}
uint8x16_t& operator =(const uint8x16_t& in) {uint8x16 = in; return uint8x16;}
uint8x8x2_t& operator =(const uint8x8x2_t& in) {uint8x8x2 = in; return uint8x8x2;}
};
)等。
首先我很热情(更容易找到在所需数据类型上运行的确切函数),然后我看到了想要以不同方式处理数据时的混乱。使用specific casting operators将永远带我。问题也得到解决here。然后我想出了一个封装成结构的联合的想法,以及一些转换和赋值运算符。
uint_128bit_t
这种方法对我有用:我可以使用vshlq_n_u32
类型的变量作为参数,并使用不同的Neon内在函数输出,例如: vuzp_u8
,vget_low_u8
,{{1}}(在这种情况下仅作为输入)。如果需要,我可以使用更多数据类型扩展它。
注意:数组是为了轻松打印变量的内容。
这是一种正确的处理方式吗?
有没有隐藏的缺陷?
我重新改造了车轮吗?
(对齐属性是否必要?)
答案 0 :(得分:3)
根据C ++标准,这种数据类型几乎没用(当然也是如此)。这是因为从联合的非活动成员中读取是未定义的行为。
然而,您的编译器可能会承诺使其工作。但是,您还没有询问任何特定的编译器,因此无法对此进行进一步评论。
答案 1 :(得分:1)
由于最初提出的方法有undefined behaviour in C++,我实现了类似的东西:
template <typename T>
struct NeonVectorType {
private:
T data;
public:
template <typename U>
operator U () {
BOOST_STATIC_ASSERT_MSG(sizeof(U) == sizeof(T),"Trying to convert to data type of different size");
U u;
memcpy( &u, &data, sizeof u );
return u;
}
template <typename U>
NeonVectorType<T>& operator =(const U& in) {
BOOST_STATIC_ASSERT_MSG(sizeof(U) == sizeof(T),"Trying to copy from data type of different size");
memcpy( &data, &in, sizeof data );
return *this;
}
};
然后:
typedef NeonVectorType<uint8x16_t> uint_128bit_t; //suitable for uint8x16_t, uint8x8x2_t, uint32x4_t, etc.
typedef NeonVectorType<uint8x8_t> uint_64bit_t; //suitable for uint8x8_t, uint32x2_t, etc.
讨论了使用memcpy here(和here),避免违反严格的别名规则。请注意in general it gets optimized away。
如果查看编辑历史记录,我已经实现了一个自定义版本,其中包含向量向量的组合运算符(例如uint8x8x2_t
)。提到了问题here。但是,由于这些数据类型被声明为数组(参见guide,第12.2.2节)并因此位于连续的内存位置,因此编译器必须正确处理memcpy
。
最后,要打印变量的内容,可以使用a function like this。
答案 2 :(得分:0)
如果你试图通过各种数据结构hackery以合理的方式避免投射,你最终会改变内存/文字,这会破坏你希望从NEON获得的任何表现。
您可以轻松地将四寄存器转换为双寄存器,但其他方式可能无法实现。
一切归结为此。在每条指令中,有几位用于索引寄存器。如果指令需要四个寄存器,它将像Q(2 * n),Q(2 * n + 1)那样二乘二计数寄存器,并且只在编码指令中使用n,(2 * n + 1)将隐含核心。如果代码中的任何一点你试图将两个双精度转换为四元组,那么你可能处于不连续的位置,迫使编译器将寄存器混合到堆栈中并返回以获得连续的布局。
我认为用不同的单词https://stackoverflow.com/a/13734838/1163019
仍然是相同的答案NEON指令设计为流式传输,您从内存中加载大块,处理它,然后存储您想要的内容。这应该是所有非常简单的机制,如果不是,你会放弃它所提供的额外性能,这将使人们问你为什么要首先利用霓虹灯让自己更难生活。
将NEON视为不可变的值类型和操作。