Pourquoi gcc remplit-il le tableau entier avec des zéros au lieu des 96 entiers restants uniquement? Les initialiseurs non nuls sont tous au début du tableau.
void *sink;
void bar() {
int a[100]{1,2,3,4};
sink = a; // a escapes the function
asm("":::"memory"); // and compiler memory barrier
// forces the compiler to materialize a[] in memory instead of optimizing away
}
MinGW8.1 et gcc9.2 font tous deux asm comme ceci ( Godbolt compiler explorer ).
# gcc9.2 -O3 -m32 -mno-sse
bar():
push edi # save call-preserved EDI which rep stos uses
xor eax, eax # eax=0
mov ecx, 100 # repeat-count = 100
sub esp, 400 # reserve 400 bytes on the stack
mov edi, esp # dst for rep stos
mov DWORD PTR sink, esp # sink = a
rep stosd # memset(a, 0, 400)
mov DWORD PTR [esp], 1 # then store the non-zero initializers
mov DWORD PTR [esp+4], 2 # over the zeroed part of the array
mov DWORD PTR [esp+8], 3
mov DWORD PTR [esp+12], 4
# memory barrier empty asm statement is here.
add esp, 400 # cleanup the stack
pop edi # and restore caller's EDI
ret
(avec SSE activé, il copierait les 4 initialiseurs avec movdqa load / store)
Pourquoi GCC ne fait-il pas lea edi, [esp+16]
et ne met -il pas (avec rep stosd
) uniquement les 96 derniers éléments, comme le fait Clang? Est-ce une optimisation manquée, ou est-ce plus efficace de le faire de cette façon? (Clang appelle en fait memset
au lieu de l'inliner rep stos
)
Note de l'éditeur: la question avait à l'origine une sortie de compilateur non optimisée qui fonctionnait de la même manière, mais le code inefficace -O0
ne prouve rien. Mais il s'avère que cette optimisation est manquée par GCC même à -O3
.
Passer un pointeur vers a
une fonction non en ligne serait une autre façon de forcer le compilateur à se matérialiser a[]
, mais en code 32 bits qui conduit à un encombrement important de l'asm. (Les arguments de pile entraînent des push, qui sont mélangés avec des magasins à la pile pour initier le tableau.)
L'utilisation de volatile a[100]{1,2,3,4}
GCC permet de créer puis de copier le tableau, ce qui est fou. Normalement, volatile
c'est bon pour regarder comment les compilateurs initialisent les variables locales ou les présentent sur la pile.
.rodata
... Je ne peux pas croire que la copie de 400 octets soit plus rapide que la mise à zéro et la définition de 8 éléments.
-O3
(ce qu'il fait). godbolt.org/z/rh_TNF
missed-optimization
mot - clé.
a[0] = 0;
puisa[0] = 1;
.