如何将__attribute __((aligned(32)))应用于int *?

在我的程序中,我需要将__attribute__(( aligned(32)))应用于int *float *我试过这样但我不确定它是否会起作用。

 int *rarray __attribute__(( aligned(32))); 

我看到了这个,但没有找到答案

所以你想告诉编译器你的指针是对齐的吗? 例如,此函数的所有调用者都将传递保证对齐的指针。 指向对齐静态或本地存储的指针,或指向C11 aligned_alloc或POSIX posix_memalign指针。 (如果这些不可用, _mm_malloc是一个选项,但在_mm_malloc结果上不保证free是安全的:你需要_mm_free )。 这允许编译器自动向量化,而无需使用一堆膨胀代码来处理未对齐的输入。

使用内在函数手动矢量化时,使用_mm256_loadu_si256_mm256_load_si256通知编译器内存是否已对齐。 传递对齐信息是加载/存储内在函数的主要点,而不是简单地解引用__m256i指针。


我不确定是否有一种可移植的方式通知编译器指针指向对齐的内存(请参阅下面提到的C ++ 11 alignas ,这似乎不适用于此,即使这是一个C ++题)。

使用GNU C __attribute__语法 ,似乎有必要使用typedef来获取要应用于指向类型的属性,而不是指针本身。 如果声明一个aligned_int类型或类似的东西,它的输入肯定更容易,也更容易阅读

 typedef __attribute__(( aligned(32))) int aligned_int; int my_func(const aligned_int *restrict a, const aligned_int *restrict b) { int sum = 0; for (int i=0 ; i<1024 ; i++) { sum += a[i] - b[i]; } return sum; } 

这个自动矢量化没有任何膨胀处理未对齐的输入(gcc 5.3与godbolt上的-O3

  pxor xmm0, xmm0 xor eax, eax .L2: psubd xmm0, XMMWORD PTR [rsi+rax] paddd xmm0, XMMWORD PTR [rdi+rax] add rax, 16 cmp rax, 4096 jne .L2 # end of vector loop ... # horizontal sum with psrldq omitted, see the godbolt link if you're curious movd eax, xmm0 ret 

如果没有aligned属性,你会得到一大块标量intro / outro代码,这将更糟糕的是-march=haswell使AVX2代码具有更宽的内循环。

Clang对未对齐输入的常规策略是使用未对齐的加载/存储,而不是完全展开的intro / outro循环。 如果没有AVX,这意味着无法将负载折叠到SSE ALU操作的内存操作数中。 奇怪的是,对齐的属性在这种情况下没有帮助clang-3.8:它仍然使用单独的movdqu加载。 请注意,clang的循环更大,因为它默认为展开4,而gcc不会在没有-funroll-loops (由-fprofile-use启用)的情况下展开。


请注意,您不能创建一个aligned_int数组 。 (请参阅有关sizeof(aligned_int)讨论的评论,以及它仍然是4,而不是32的事实)。 GNU C拒绝将其视为带有填充的int ,因此使用gcc 5.3:

 static aligned_int arr[1024]; // error: alignment of array elements is greater than element size int tmp = sizeof(arr); 

clang-3.8编译,并将tmp初始化为4096。

gcc文档声称在结构上使用aligned属性可以让你创建一个数组,这是主要的用例之一。 但是,正如@ user3528438在注释中指出的那样, 情况并非如此 :您在尝试声明一个aligned_int数组时会遇到相同的错误。 自2005年以来一直如此。


要定义对齐的局部或静态/全局数组aligned属性应该应用于整个数组,而不是应用于每个元素。

在便携式C ++ 11中,你可以使用像alignas(32) int myarray[1024]; 。 另请参阅使用alignas语法进行拼写 :它似乎只对自身对齐事物很有用,而不是声明指针指向对齐的内存。 std::align更像是((uintptr_t)ptr) & ~63或者其他东西:强制对齐指针而不是告诉编译器它已经对齐了。

 alignas(32) int foo[1000]; // C++11 syntax, no C11 equivalent __attribute__((aligned(32))) int foo[1000]; // GNU C 

如果您想要可移植性,CPP宏可用于在GNU C __attribute__语法和MSVC __declspec语法之间进行选择以进行对齐。

例如,使用此代码声明一个局部数组具有比堆栈指针更多的对齐,编译器必须创建空间,然后使用AND指向堆栈指针以获得对齐的指针:

 void foo(int *p); void bar(void) { __attribute__((aligned(32))) int a[1000]; foo (a); } 

编译为(clang-3.8 -O3 -std=gnu11 for x86-64)

  push rbp mov rbp, rsp # stack frame with base pointer since we're doing unpredictable things to rsp and rsp, -32 # 32B-align the stack sub rsp, 4032 # reserve up to 32B more space than needed lea rdi, [rsp] # this is weird: mov rdi,rsp is a shorter insn to set up foo's arg call foo mov rsp, rbp pop rbp ret 

gcc(晚于4.8.2)使得显着更大的代码无缘无故地做了一堆额外的工作,最奇怪的是push QWORD PTR [r10-8]将一些堆栈内存复制到堆栈上的另一个地方。 (在godbolt链接上查看:翻转到gcc)。