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

ADM*_*DMS 4 c gcc simd

在我的程序中,我需要申请__attribute__(( aligned(32)))一个int *float * 我试过这样但我不确定它会起作用。

int  *rarray __attribute__(( aligned(32)));
Run Code Online (Sandbox Code Playgroud)

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

Pet*_*des 7

所以你想告诉编译器你的指针是对齐的吗?例如,该函数的所有调用者都将传递保证对齐的指针。指向对齐的静态或本地存储的指针,或者它们从 C11aligned_alloc或 POSIX获得的指针posix_memalign。(如果这些不可用,_mm_malloc是一种选择,但free不能保证_mm_malloc结果是安全的:您需要_mm_free)。这允许编译器自动矢量化,而无需编写一堆臃肿的代码来处理未对齐的输入。

当您使用内在函数手动矢量化时,您使用_mm256_loadu_si256_mm256_load_si256通知编译器内存是否对齐。与简单地取消引用__m256i指针相反,通信对齐信息是加载/存储内在函数的主要点。


我认为没有一种可移植的方式来通知编译器指针指向对齐的内存。(C11 / C++11alignas似乎无法做到这一点,见下文)。

使用GNU C__attribute__语法,似乎有必要使用 atypedef来获取要应用于指向类型的属性,而不是指针本身。如果你声明一个aligned_int类型或其他东西,肯定会更容易打字和阅读

// Only helps GCC, not clang or ICC
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;
}
Run Code Online (Sandbox Code Playgroud)

这会自动矢量化,没有任何膨胀来处理未对齐的输入(gcc 5.3 with -O3on Godbolt)

    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
Run Code Online (Sandbox Code Playgroud)

如果没有对齐属性,您将获得一大块标量介绍/输出代码,如果-march=haswell使用更宽的内部循环制作 AVX2 代码,情况会更糟。


Clang 对未对齐输入的正常策略是使用未对齐的加载/存储,而不是完全展开的 intro/outro 循环。如果没有 AVX,这意味着无法将负载折叠到 SSE ALU 操作的内存操作数中。

aligned属性对 clang 没有帮助(最近测试为 clang7.0):它仍然使用单独的movdqu负载。 请注意,clang 的循环更大,因为它默认展开 4,而 gcc 根本不会展开-funroll-loops(由 启用-fprofile-use)。

但请注意,此aligned_inttypedef适用于 GCC 本身,不适用于 clang 或 ICCgcc 内存对齐 pragma有另一个例子。

__builtin_assume_aligned 是较嘈杂的语法,但确实适用于支持 GNU C 扩展的所有编译器。

请参阅如何告诉 GCC 指针参数始终是双字对齐的?


请注意,您无法创建aligned_int. (请参阅有关讨论的评论sizeof(aligned_int),以及它仍然是 4,而不是 32 的事实)。GNU C 拒绝将其视为int-with-padding,因此对于 gcc 5.3:

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

clang-3.8 编译它,并初始化tmp为 4096。大概是因为它只是完全忽略了aligned该上下文中的属性,而不是执行 gcc 所做的任何魔术来获得比其所需对齐更窄的类型。(因此,实际上只有每四个元素具有该对齐方式。)

gcc的文档要求,使用aligned上一个struct属性确实让你做一个数组,并认为这是主要的用例之一。然而,正如@ user3528438在评论中指出,这是没有的情况:你得到同样的错误试图声明数组作为时aligned_int自 2005 年以来,情况一直如此。


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

在便携式 C11 和 C++11 中,您可以使用诸如alignas(32) int myarray[1024];. 另请参阅使用 alignas 语法苦苦挣扎:它似乎只对对齐事物本身有用,而不是声明指针指向对齐的内存。 std::align更像是((uintptr_t)ptr) & ~63什么:强行对齐一个指针而不是告诉编译器它已经对齐了。

// declaring aligned storage for arrays
#ifndef __cplusplus
#include <stdalign.h>   // for C11: defines alignas() using _Alignas()
#endif                  // C++11 defines alignas without any headers

// works for global/static or local  (aka automatic storage)
alignas(32) int foo[1000];      // portable ISO C++11 and ISO C11 syntax


// __attribute__((aligned(32))) int foo[1000];  // older GNU C
// __declspec something  // older MSVC
Run Code Online (Sandbox Code Playgroud)

请参阅有关 cppreference的C11alignas()文档

如果您希望在不支持 C11 的旧编译器上具有可移植性,CPP 宏可用于在 GNU C__attribute__语法和 MSVC__declspec语法之间进行选择以进行对齐。

例如,使用此代码声明一个本地数组的对齐比可以为堆栈指针假定的对齐更多,编译器必须腾出空间,然后腾出AND堆栈指针以获得对齐的指针:

void foo(int *p);
void bar(void) {
  __attribute__((aligned(32))) int a[1000];
  foo (a);
}
Run Code Online (Sandbox Code Playgroud)

编译为 (clang-3.8 -O3 -std=gnu11for 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
Run Code Online (Sandbox Code Playgroud)

gcc(4.8.2 之后的版本)让大得多的代码无缘无故地做了一堆额外的工作,最奇怪的是push QWORD PTR [r10-8]将一些堆栈内存复制到堆栈上的另一个位置。(在 Godbolt 链接上查看:flip clang to gcc)。

  • 我在这里尝试过 https://ideone.com/oxGyiN 。因此,似乎 GCC 已决定不完全实现对齐类型的 `sizeof()` 以包含额外的填充,这基本上禁止声明此类类型的数组。这个错误似乎是在 2005 https://gcc.gnu.org/ml/gcc-patches/2005-09/msg01853.html 中添加的。现在,11 年后,很难争论这是错误还是功能。 (2认同)