Dans mon programme, je dois appliquer __atsortingbute__(( aligned(32)))
sur un int *
ou un float *
j’ai essayé comme ça, mais je ne suis pas sûr que cela fonctionnera.
int *rarray __atsortingbute__(( aligned(32)));
J’ai vu ça mais je n’ai pas trouvé la réponse
Donc, vous voulez dire au compilateur que vos pointeurs sont alignés? Par exemple, tous les appelants de cette fonction passeront par des pointeurs dont l’alignement est garanti. Soit des pointeurs vers un stockage statique ou local aligné, soit des pointeurs obtenus de C11 aligned_alloc
ou POSIX posix_memalign
. (Si ceux-ci ne sont pas disponibles, _mm_malloc
est une option, mais free
n’est pas sûr d’être sûr des résultats _mm_malloc
: vous avez besoin de _mm_free
). Cela permet au compilateur de s’auto-vectoriser sans créer un tas de code saturé pour gérer les entrées non alignées.
Lorsque vous vectorisez manuellement avec des éléments insortingnsèques, vous utilisez _mm256_loadu_si256
ou _mm256_load_si256
pour indiquer au compilateur si la mémoire est alignée ou non. La communication des informations d’alignement est le point principal des éléments insortingnsèques de chargement / stockage, par opposition au simple déréférencement des pointeurs __m256i
.
Je ne sais pas s’il existe un moyen portable d’informer le compilateur qu’un pointeur pointe sur une mémoire alignée (voir ci-dessous la mention d’ alignas
C ++ 11, qui ne semble pas être utilisable pour cela, même s’il s’agissait d’un C ++. question).
Avec la syntaxe __atsortingbute__
GNU C , il semble nécessaire d’utiliser un typedef
pour que l’atsortingbut s’applique au type pointé, plutôt qu’au pointeur lui-même. Il est certainement plus facile de taper et de lire si vous déclarez un type aligned_int
ou autre .
typedef __atsortingbute__(( aligned(32))) int aligned_int; int my_func(const aligned_int *ressortingct a, const aligned_int *ressortingct b) { int sum = 0; for (int i=0 ; i<1024 ; i++) { sum += a[i] - b[i]; } return sum; }
ceci auto-vectorise sans aucun gonflement pour gérer les entrées non alignées (gcc 5.3 avec -O3
sur 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
Sans l'atsortingbut aligné, vous obtenez un gros bloc de code scalaire intro / outro, ce qui serait encore pire avec -march=haswell
pour créer du code AVX2 avec une boucle interne plus large.
La stratégie normale de Clang pour les entrées non alignées consiste à utiliser des charges / magasins non alignés, au lieu de boucles intro / outro entièrement déroulées. Sans AVX, cela signifie que les charges ne pourraient pas être repliées dans des opérandes en mémoire pour les opérations SSE ALU. Étrangement, l'atsortingbut aligné n'aide pas clang-3.8 dans ce cas: il utilise toujours des charges movdqu
séparées. Notez que la boucle de clang est plus grande parce qu'elle se déroule par défaut à 4, alors que gcc ne se déroule pas du tout sans -funroll-loops
(activé par -fprofile-use
).
Notez que vous ne pouvez pas faire un tableau de aligned_int
. (Voir les commentaires pour une discussion de sizeof(aligned_int)
, et le fait qu'il en rest 4, pas 32). GNU C refuse de le traiter comme un int
-with-padding, donc avec 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 le comstack et initialise tmp
à 4096.
Les documents gcc affirment que l'utilisation de l'atsortingbut aligned
sur une structure vous permet de créer un tableau, ce qui est l'un des principaux cas d'utilisation. Toutefois, comme @ user3528438 l'a souligné dans les commentaires, il en va autrement: vous obtenez la même erreur que lorsque vous tentez de déclarer un tableau de aligned_int
. C'est le cas depuis 2005 .
Pour définir des tableaux alignés locaux ou statiques / globaux , l'atsortingbut aligned
doit être appliqué à l'ensemble du tableau plutôt qu'à tous les éléments.
Dans C ++ 11 portable, vous pouvez utiliser des éléments tels que alignas(32) int myarray[1024];
. Voir aussi Lutte contre la syntaxe Alignas : elle ne semble utile que pour aligner des éléments eux-mêmes, sans déclarer que les pointeurs pointent vers une mémoire alignée. std::align
est plus comme ((uintptr_t)ptr) & ~63
ou quelque chose comme ça: aligner de force un pointeur plutôt que de dire au compilateur qu'il était déjà aligné.
alignas(32) int foo[1000]; // C++11 syntax, no C11 equivalent __atsortingbute__((aligned(32))) int foo[1000]; // GNU C
Les macros CPP peuvent être utiles pour choisir entre la syntaxe GNU C __atsortingbute__
et la syntaxe __atsortingbute__
__declspec
pour l’alignement si vous voulez la portabilité.
Par exemple, avec ce code qui déclare un tableau local avec plus d'alignement que ce qui peut être supposé pour le pointeur de stack, le compilateur doit créer de l'espace puis AND
le pointeur de stack pour obtenir un pointeur aligné:
void foo(int *p); void bar(void) { __atsortingbute__((aligned(32))) int a[1000]; foo (a); }
comstack pour (clang-3.8 -O3 -std=gnu11
pour 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 (plus tard que 4.8.2) rend le code beaucoup plus volumineux, sans raison, le plus étrange étant push QWORD PTR [r10-8]
pour copier de la mémoire dans un autre emplacement de la stack. (allez voir ça sur le lien de godbolt: flip clang to gcc).