perf report montre que cette fonction “__memset_avx2_unaligned_erms” a un overhead. cela signifie-t-il que la mémoire n’est pas alignée?

J’essaie de profiler mon code C ++ à l’aide de l’outil perf. L’implémentation contient du code avec les instructions SSE / AVX / AVX2. En plus de ce code est compilé avec -O3 -mavx2 -march=native flags -O3 -mavx2 -march=native . Je crois __memset_avx2_unaligned_erms fonction __memset_avx2_unaligned_erms est une implémentation libc de memset . perf montre que cette fonction a des frais généraux considérables. Le nom de la fonction indique que la mémoire n’est pas alignée, mais dans le code, __atsortingbute__((aligned (x))) explicitement la mémoire à l’aide de la macro __atsortingbute__((aligned (x))) GCC __atsortingbute__((aligned (x))) Quelle peut être la raison pour laquelle cette fonction entraîne un surcoût important ainsi que la version non alignée est appelé bien que la mémoire soit alignée explicitement?

J’ai joint l’exemple de rapport en tant qu’image. entrez la description de l'image ici

Non, ça ne va pas. Cela signifie que la stratégie de memset choisie par la glibc sur ce matériel est celle qui n’essaie pas d’éviter complètement les access alignés, dans les cas de petite taille . (La glibc sélectionne une implémentation de memset au moment de la résolution du symbole de l’éditeur de liens dynamic, de sorte qu’elle est dissortingbuée au moment de l’exécution sans temps système supplémentaire après le premier appel.)

Si votre mémoire tampon est en fait alignée et que la taille est un multiple de la largeur du vecteur, tous les access seront alignés et il n’y aura pratiquement pas de surcharge. (L’utilisation de vmovdqu avec un pointeur aligné lors de l’exécution est exactement équivalente à vmovdqa sur tous les processeurs vmovdqa en charge AVX.)

Pour les mémoires tampons volumineuses, il aligne toujours le pointeur avant la boucle principale au cas où elle ne serait pas alignée, au prix de quelques instructions supplémentaires par rapport à une implémentation qui ne fonctionnerait que pour des pointeurs alignés sur 32 octets. (Mais on dirait qu’il utilise rep stosb sans aligner le pointeur, si ça va rep stosb du tout.)

gcc + glibc ne dispose pas d’une version spéciale de memset appelée uniquement avec des pointeurs alignés. (Ou plusieurs versions spéciales pour différentes garanties d’alignement). L’implémentation non alignée AVX2 de GLIBC fonctionne parfaitement pour les entrées alignées et non alignées.


Il est défini dans glibc/sysdeps/x86_64/multiarch/memset-avx2-unaligned-erms.S , qui définit un couple de macros (comme définir la taille du vecteur à 32), puis #includes "memset-vec-unaligned-erms.S" .

Le commentaire dans le code source dit:

 /* memset is implemented as: 1. Use overlapping store to avoid branch. 2. If size is less than VEC, use integer register stores. 3. If size is from VEC_SIZE to 2 * VEC_SIZE, use 2 VEC stores. 4. If size is from 2 * VEC_SIZE to 4 * VEC_SIZE, use 4 VEC stores. 5. If size is more to 4 * VEC_SIZE, align to 4 * VEC_SIZE with 4 VEC stores and store 4 * VEC at a time until done. */ 

L’alignement réel avant la boucle principale est effectué après quelques magasins de vmovdqu vectoriels (qui ne comportent aucune pénalité s’ils sont utilisés sur des données alignées: https://agner.org/optimize/ ):

 L(loop_start): leaq (VEC_SIZE * 4)(%rdi), %rcx # rcx = input pointer + 4*VEC_SIZE VMOVU %VEC(0), (%rdi) # store the first vector andq $-(VEC_SIZE * 4), %rcx # align the pointer ... some more vector stores ... and stuff, including storing the last few vectors I think addq %rdi, %rdx # size += start, giving an end-pointer andq $-(VEC_SIZE * 4), %rdx # align the end-pointer L(loop): # THE MAIN LOOP VMOVA %VEC(0), (%rcx) # vmovdqa = alignment required VMOVA %VEC(0), VEC_SIZE(%rcx) VMOVA %VEC(0), (VEC_SIZE * 2)(%rcx) VMOVA %VEC(0), (VEC_SIZE * 3)(%rcx) addq $(VEC_SIZE * 4), %rcx cmpq %rcx, %rdx jne L(loop) 

Donc, avec VEC_SIZE = 32, il aligne le pointeur sur 128. C’est overkill; les lignes de cache ont une taille de 64 octets et un alignement sur la largeur du vecteur devrait suffire.

Il a également un seuil pour l’utilisation de rep stos s’il est activé et si la taille de la mémoire tampon est> 2kiB sur les CPU avec ERMSB. ( REP MOVSB ​​amélioré pour la mémoire ).