[英]How can I be sure that this C structure is packed both on 32 bit and 64 bit systems? Is “__attribute__((packed))” always necessary?
[英]How can I apply __attribute__(( aligned(32))) to an 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
指針。
我不認為有一種可移植的方式來通知編譯器指針指向對齊的內存。 (C11 / C ++ 11 alignas
似乎無法做到這一點,見下文)。
使用GNU C __attribute__
語法 ,似乎有必要使用typedef
來獲取要應用於指向類型的屬性,而不是指針本身。 如果你聲明一個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;
}
此自動向量化而沒有任何膨脹用於處理未對齊的輸入(GCC 5.3 -O3
上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
如果沒有aligned屬性,你會得到一大塊標量intro / outro代碼,這會更糟糕的是-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 memory alignment pragma有另一個例子。
__builtin_assume_aligned
是更嘈雜的語法,但適用於所有支持GNU C擴展的編譯器。 請注意,您不能創建一個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.大概是因為它只是完全忽略了該上下文中的aligned
屬性,沒有做任何魔法gcc所做的任何一種類型比它所需的對齊更窄的類型。 (因此,只有每四個元素實際上具有該對齊。)
gcc文檔聲稱在結構上使用aligned
屬性可以讓你創建一個數組,這是主要的用例之一。 但是,正如@ 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
有關cppreference的信息,請參閱C11 alignas()
文檔 。
如果您希望在不支持C11的舊編譯器上實現可移植性,則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)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.