Considérez cette simple boucle :
float f(float x[]) {
float p = 1.0;
for (int i = 0; i < 959; i++)
p += 1;
return p;
}
Si vous compilez avec gcc 7 (snapshot) ou clang (trunk) avec -march=core-avx2 -Ofast
vous obtenez quelque chose de très similaire à.
.LCPI0_0:
.long 1148190720 # float 960
f: # @f
vmovss xmm0, dword ptr [rip + .LCPI0_0] # xmm0 = mem[0],zero,zero,zero
ret
En d'autres termes, il fixe simplement la réponse à 960 sans boucle.
Cependant, si vous changez le code en :
float f(float x[]) {
float p = 1.0;
for (int i = 0; i < 960; i++)
p += 1;
return p;
}
L'assemblage produit effectue réellement la somme de la boucle ? Par exemple, clang donne :
.LCPI0_0:
.long 1065353216 # float 1
.LCPI0_1:
.long 1086324736 # float 6
f: # @f
vmovss xmm0, dword ptr [rip + .LCPI0_0] # xmm0 = mem[0],zero,zero,zero
vxorps ymm1, ymm1, ymm1
mov eax, 960
vbroadcastss ymm2, dword ptr [rip + .LCPI0_1]
vxorps ymm3, ymm3, ymm3
vxorps ymm4, ymm4, ymm4
.LBB0_1: # =>This Inner Loop Header: Depth=1
vaddps ymm0, ymm0, ymm2
vaddps ymm1, ymm1, ymm2
vaddps ymm3, ymm3, ymm2
vaddps ymm4, ymm4, ymm2
add eax, -192
jne .LBB0_1
vaddps ymm0, ymm1, ymm0
vaddps ymm0, ymm3, ymm0
vaddps ymm0, ymm4, ymm0
vextractf128 xmm1, ymm0, 1
vaddps ymm0, ymm0, ymm1
vpermilpd xmm1, xmm0, 1 # xmm1 = xmm0[1,0]
vaddps ymm0, ymm0, ymm1
vhaddps ymm0, ymm0, ymm0
vzeroupper
ret
Pourquoi est-ce le cas et pourquoi est-ce exactement la même chose pour clang et gcc ?
La limite pour la même boucle si vous remplacez float
avec double
est de 479. C'est encore le cas pour gcc et clang.
Mise à jour 1
Il s'avère que gcc 7 (snapshot) et clang (trunk) se comportent très différemment. clang optimise les boucles pour toutes les limites inférieures à 960 pour autant que je puisse dire. gcc d'autre part est sensible à la valeur exacte et n'a pas de limite supérieure. Par exemple, il n'est pas d'optimiser la boucle lorsque la limite est de 200 (ainsi que de nombreuses autres valeurs) mais elle fait lorsque la limite est de 202 et 20002 (ainsi que de nombreuses autres valeurs).