ultima objective logo descargar caracteristicas c++ c gcc assembly x86-64

objective - Grandes diferencias en la generación de código GCC al compilar como C++ vs. C



objective c ultima version (1)

He estado jugando un poco con el ensamblaje x86-64 tratando de aprender más sobre las diversas extensiones SIMD que están disponibles (MMX, SSE, AVX).

Para ver cómo GCC traduce las diferentes construcciones de C o C ++ en código de máquina, he estado usando el Explorador de compiladores, que es una herramienta excelente.

Durante una de mis "sesiones de juego", quería ver cómo GCC podía optimizar una inicialización de tiempo de ejecución simple de una matriz de enteros. En este caso traté de escribir los números 0 a 2047 en una matriz de 2048 enteros sin signo.

El código se ve de la siguiente manera:

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

Si -O3 -mavx512f -mtune=intel las optimizaciones y las instrucciones AVX-512 -O3 -mavx512f -mtune=intel GCC 6.3 genera un código realmente inteligente :)

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

Sin embargo, cuando probé lo que se generaría si se compilara el mismo código utilizando el compilador C de GCC agregando las banderas -xc , realmente me sorprendió.

Esperaba resultados similares, si no idénticos, pero el compilador C parece generar código de máquina mucho más complicado y presumiblemente también mucho más lento. El ensamblaje resultante es demasiado grande para pegarlo aquí en su totalidad, pero se puede ver en godbolt.org siguiendo this enlace.

Un fragmento del código generado, líneas 58 a 83, se puede ver a continuación:

.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]

Como puede ver, este código tiene muchos movimientos y saltos complicados y, en general, se siente como una forma muy compleja de realizar una inicialización de matriz simple.

¿Por qué hay una gran diferencia en el código generado?

¿El compilador C ++ de GCC es mejor en general en la optimización de código que es válido tanto en C como en C ++ en comparación con el compilador C?


El código adicional es para manejar la desalineación porque la instrucción utilizada, vmovdqa64 , requiere una alineación de 64 bytes.

Mi prueba muestra que, aunque el estándar no lo hace, gcc permite una definición en otro módulo para anular la que se encuentra aquí en el modo C. Esa definición solo puede cumplir con los requisitos de alineación básica (4 bytes), por lo que el compilador no puede confiar en la alineación más grande. Técnicamente, gcc emite una directiva de conjunto .comm para esta definición tentativa, mientras que una definición externa usa un símbolo normal en la sección .data . Durante la vinculación, este símbolo tiene prioridad sobre el .comm .

Tenga en cuenta si cambia el programa para usar el extern unsigned int buffer[2048]; entonces incluso la versión de C ++ tendrá el código agregado. Por el contrario, convirtiéndolo en un static unsigned int buffer[2048]; convertirá la versión C en la versión optimizada.