从字节数组加载uint8x16_t的对齐要求?

时间:2016-05-28 18:34:31

标签: arm memory-alignment neon intrinsics

我们在Debug版本下有一个断言触发,用于检查对齐。断言用于使用uint8x16_t加载到vld1q_u8的字节数组。当断言触发时,我们没有观察到SIG_BUS

以下是代码中的用法:

const byte* input = ...;
...

assert(IsAlignedOn(input, GetAlignmentOf(uint8x16_t));
uint64x2_t message = vreinterpretq_u64_u8(vld1q_u8(input));

我也尝试了以下内容,并且断言触发uint8_t*的对齐:

assert(IsAlignedOn(input, GetAlignmentOf(uint8_t*));
uint64x2_t message = vreinterpretq_u64_u8(vld1q_u8(input));

使用uint8x16_t将字节数组加载到vld1q_u8时,字节数组有哪些对齐要求?

在上面的代码中,input是函数参数。 IsAlignedOn检查其两个参数的对齐方式,确保第一个参数与至少第二个对齐。 GetAlignmentOf是一个抽象,用于检索类型或变量的对齐方式。

uint8x16_tuint64x2_t是128位ARM NEON向量数据类型expected to be placed in a Q registervld1q_u8是一条NEON伪指令,预计会被编译成VLD1.8指令。 vreinterpretq_u64_u8是一个NEON伪指令,可以简化数据类型的使用。

3 个答案:

答案 0 :(得分:4)

编写直接汇编程序(内联文件或外部文件)时,您可以选择是否要指定对齐(例如vld1.8 {q0}, [r0, :64])或将其保留(例如vld1.8 {q0}, [r0])。如果没有指定,它根本不需要任何特定的对齐,如Dric512所说。

当通过内在函数使用vld1q_u8时,你实际上并没有指定对齐,所以据我所知,编译器不会假定它,并产生没有对齐规范的指令。我不确定某些编译器是否可以推断出实际保证对齐的一些情况,并在这些情况下使用对齐说明符。 (在这种特殊情况下,gcc,clang和MSVC似乎都生成vld1.8而没有对齐说明符。)

请注意,这只是32位手臂的问题;在AArch64中,ld1指令没有对齐说明符。但即使在那里,对齐仍然显然有帮助,如果你使用未对齐的地址,你的性能会更差。

答案 1 :(得分:3)

将{16}字节加载到Quad寄存器的 import csv # expected codes and their replacements CODES = { '50': 'order now', '999': 'no order necessary', '500': 'order next month', 'amount available': 'order timeline' } # you can multiple with statements in one with open('input.csv', 'r') as fp_in, open('output.csv', 'w') as fp_out: reader = csv.reader(fp_in) writer = csv.writer(fp_out) for row in reader: val = row[2].strip() # deal with the weird spaces val = CODES.get(val, val) # see if this is a known constant, and replace if exists row[2] = ' ' + val # add space back and insert back into row writer.writerow(row) 指令的自然对齐是一个字节。这意味着即使不允许未对齐的传输,该指令也不会出错。

所以看起来这个特定的断言是不正确的。

答案 2 :(得分:2)

从另一端看这个,从一个示例编译器(Visual Studio 2015的arm_neon.h)的角度来看,这是该类型的实际定义:

typedef union __declspec(intrin_type) _ADVSIMD_ALIGN(8) __n128
{
     unsigned __int64   n128_u64[2];
     unsigned __int32   n128_u32[4];
     unsigned __int16   n128_u16[8];
     unsigned __int8    n128_u8[16];
     __int64            n128_i64[2];
     __int32            n128_i32[4];
     __int16            n128_i16[8];
     __int8             n128_i8[16];
     float              n128_f32[4];

    struct
    {
        __n64  low64;
        __n64  high64;
    } DUMMYNEONSTRUCT;

} __n128;

...

typedef __n128   int8x16_t;

所以,至少在Windows平台上,由于该联合,所需要的__int64AAPCS的对齐意味着8个字节(即使没有 - 非常具有挑战性地猜测_ADVSIMD_ALIGN(8)可能意味着什么......)

但是,它比那更直接,因为事实证明AAPCS实际上确实直接使用了容器化矢量(§4.1)中的矢量类型定义。 2):

  

容器化矢量的内容对于大多数过程调用标准是不透明的:其布局中唯一定义的方面是存储器格式(基本类型存储在存储器中的方式)与不同类型的寄存器之间的映射。过程调用接口。

换句话说,在ABI级别,矢量类型是矢量类型,无论其中可能包含或不包含什么,并且64位和128位容器化矢量都需要8字节对齐,因为ABI说所以(§4.1)。因此,无论底层指令可能具备什么功能,微软的实现都不像我最初推测的那样过于严格,它只是符合要求。 八个是你要对齐的数字,对齐的数量应该是八个

另一方面,vld1q_u8()的参数是uint8_t const *,其指向的数据没有对齐要求,因此断言它满足8字节对齐可能会失败很多。