使用union(封装在struct中)来绕过neon数据类型的转换

时间:2015-03-23 11:05:27

标签: c++ gcc arm vectorization neon

我使用SSE进行了矢量化内在函数的第一种方法,其中基本上只有一种数据类型__m128i。切换到Neon我发现数据类型和函数原型更加具体,例如uint8x16_t(向量为16 unsigned char),uint8x8x2_t(2个向量,每个unsigned charuint32x4_tuint32_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_u8vget_low_u8,{{1}}(在这种情况下仅作为输入)。如果需要,我可以使用更多数据类型扩展它。 注意:数组是为了轻松打印变量的内容。

这是一种正确的处理方式吗? 有没有隐藏的缺陷?
我重新改造了车轮吗? (对齐属性是否必要?)

3 个答案:

答案 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视为不可变的值类型和操作。