35 votes

Grandes différences dans la génération de code GCC lors de la compilation en C ++ vs C

J'ai joué un peu avec le x86-64 assemblée essayer d'en apprendre plus sur les différents SIMD extensions qui sont disponibles (MMX, SSE, AVX).

Afin de voir comment les différents C ou C++ constructions sont traduit en code machine par GCC j'ai été en utilisant le Compilateur Explorer qui est un superbe outil.

Lors d'une de mes séances de jeu " je voulais voir comment CCG pourrait optimiser un simple moment de l'exécution de l'initialisation d'un tableau d'entiers. Dans ce cas, j'ai essayé d'écrire les nombres de 0 à 2047 à un tableau de 2048 des entiers non signés.

Le code se présente comme suit:

unsigned int buffer[2048];

void setup()
{
  for (unsigned int i = 0; i < 2048; ++i)
  {
    buffer[i] = i;
  }
}

Si je activer les optimisations et AVX-512 instructions -O3 -mavx512f -mtune=intel GCC 6.3 génère de très intelligent de code :)

setup():
        mov     eax, OFFSET FLAT:buffer
        mov     edx, OFFSET FLAT:buffer+8192
        vmovdqa64       zmm0, ZMMWORD PTR .LC0[rip]
        vmovdqa64       zmm1, ZMMWORD PTR .LC1[rip]
.L2:
        vmovdqa64       ZMMWORD PTR [rax], zmm0
        add     rax, 64
        cmp     rdx, rax
        vpaddd  zmm0, zmm0, zmm1
        jne     .L2
        ret
buffer:
        .zero   8192
.LC0:
        .long   0
        .long   1
        .long   2
        .long   3
        .long   4
        .long   5
        .long   6
        .long   7
        .long   8
        .long   9
        .long   10
        .long   11
        .long   12
        .long   13
        .long   14
        .long   15
.LC1:
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16

Cependant, lorsque j'ai testé ce que pourrait être générée si le même code a été compilé à l'aide de la GCC compilateur C en ajoutant les drapeaux -x c j'ai été vraiment surpris.

Je m'attendais similaire, sinon identique, les résultats, mais le compilateur C semble générer beaucoup plus compliqué et sans doute aussi beaucoup plus lent en code machine. L'assemblage qui en résulte est trop grand pour coller ici dans son intégralité, mais il peut être consulté à godbolt.org en suivant ce lien.

Un extrait du code généré, lignes 58 à 83, peut être vu ci-dessous:

.L2:
        vpbroadcastd    zmm0, r8d
        lea     rsi, buffer[0+rcx*4]
        vmovdqa64       zmm1, ZMMWORD PTR .LC1[rip]
        vpaddd  zmm0, zmm0, ZMMWORD PTR .LC0[rip]
        xor     ecx, ecx
.L4:
        add     ecx, 1
        add     rsi, 64
        vmovdqa64       ZMMWORD PTR [rsi-64], zmm0
        cmp     ecx, edi
        vpaddd  zmm0, zmm0, zmm1
        jb      .L4
        sub     edx, r10d
        cmp     r9d, r10d
        lea     eax, [r8+r10]
        je      .L1
        mov     ecx, eax
        cmp     edx, 1
        mov     DWORD PTR buffer[0+rcx*4], eax
        lea     ecx, [rax+1]
        je      .L1
        mov     esi, ecx
        cmp     edx, 2
        mov     DWORD PTR buffer[0+rsi*4], ecx
        lea     ecx, [rax+2]

Comme vous pouvez le voir, ce code a beaucoup de mouvements compliqués, de sauts et en général se sent comme très complexe de la façon de procéder à une simple initialisation de tableau.

Pourquoi est-il une si grande différence dans le code généré?

Est la GCC C++compilateur mieux en général à l'optimisation de code qui est valable en C et C++, comparativement à la compilateur C?

40voto

Jester Points 15625

Le code supplémentaire est pour la manutention, le désalignement parce que l'instruction, vmovdqa64, nécessite de 64 octets d'alignement.

Mes tests montre que même si la norme n'est pas, gcc ne permettent une définition dans un autre module pour remplacer celui là quand C mode. Cette définition pourrait seulement se conformer à la base de l'alignement des exigences (4 octets) donc le compilateur ne peut pas compter sur le plus grand alignement. Techniquement, gcc émet un .comm de l'assemblée directive pour cette tentative de définition, une définition externe utilise un symbole normal de la .data section. Au cours de relier ce symbole prend la priorité sur l' .comm on.

Remarque si vous modifiez le programme pour utiliser extern unsigned int buffer[2048]; alors même que le C++ version sera l'ajout d'un code. A l'inverse, le rendant static unsigned int buffer[2048]; va tourner à la version de C dans la optimisé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X