在我的程序中,我需要申请__attribute__(( aligned(32)))
一个int *
或float *
我试过这样但我不确定它会起作用。
int *rarray __attribute__(( aligned(32)));
Run Code Online (Sandbox Code Playgroud)
我看到了这个,但没有找到答案
所以你想告诉编译器你的指针是对齐的吗?例如,该函数的所有调用者都将传递保证对齐的指针。指向对齐的静态或本地存储的指针,或者它们从 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 -O3
on 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_int
typedef仅适用于 GCC 本身,不适用于 clang 或 ICC。 gcc 内存对齐 pragma有另一个例子。
__builtin_assume_aligned
是较嘈杂的语法,但确实适用于支持 GNU C 扩展的所有编译器。请注意,您无法创建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=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
Run Code Online (Sandbox Code Playgroud)
gcc(4.8.2 之后的版本)让大得多的代码无缘无故地做了一堆额外的工作,最奇怪的是push QWORD PTR [r10-8]
将一些堆栈内存复制到堆栈上的另一个位置。(在 Godbolt 链接上查看:flip clang to gcc)。
归档时间: |
|
查看次数: |
4590 次 |
最近记录: |