2012-06-04 11 views
5

compilé el siguiente código C:Acerca del código compilado gcc-x86_64 y optimización de código C

typedef struct { 
    long x, y, z; 
} Foo; 

long Bar(Foo *f, long i) 
{ 
    return f[i].x + f[i].y + f[i].z; 
} 

con el comando gcc -S -O3 test.c. Esta es la función de la barra en la salida:

.section __TEXT,__text,regular,pure_instructions 
    .globl _Bar 
    .align 4, 0x90 
_Bar: 
Leh_func_begin1: 
    pushq %rbp 
Ltmp0: 
    movq %rsp, %rbp 
Ltmp1: 
    leaq (%rsi,%rsi,2), %rcx 
    movq 8(%rdi,%rcx,8), %rax 
    addq (%rdi,%rcx,8), %rax 
    addq 16(%rdi,%rcx,8), %rax 
    popq %rbp 
    ret 
Leh_func_end1: 

Tengo algunas preguntas acerca de este código de montaje:

  1. ¿Cuál es el propósito de "pushq %rbp", "movq %rsp, %rbp", y "popq %rbp", si no se usa rbp ni rsp en el cuerpo de la función?
  2. ¿Por qué rsi y rdi contienen automáticamente los argumentos para la función C (i y f, respectivamente) sin leerlos de la pila?
  3. Intenté aumentar el tamaño de Foo a 88 bytes (11 long s) y la instrucción leaq se convirtió en imulq. ¿Tendría sentido diseñar mis estructuras para tener tamaños "redondos" para evitar las instrucciones de multiplicación (para optimizar el acceso a la matriz)? La instrucción leaq fue sustituido por:

    imulq $88, %rsi, %rcx 
    

Respuesta

7
  1. La función es simplemente construir su propio marco de pila con estas instrucciones. No hay nada realmente inusual en ellos. Sin embargo, debe tener en cuenta que, debido al tamaño pequeño de esta función, probablemente estará en línea cuando se use en el código. Sin embargo, siempre se requiere que el compilador produzca una versión "normal" de la función. Además, lo que @ouah dijo en su respuesta.

  2. Esto es porque así es como AMD64 ABI especifica que los argumentos se deben pasar a las funciones.

    Si la clase es INTEGER, el siguiente registro disponible de la secuencia % RDI,% RSI,% RDX,% rcx,% r8 y r9% se utiliza.

    Página 20, AMD64 ABI Proyecto 0.99.5 - 3 de septiembre, 2010

  3. Esto no es directamente relacionado con el tamaño de la estructura, en lugar - la dirección absoluta de que la función tiene que acceder. Si el tamaño de la estructura es de 24 bytes, f es la dirección de la matriz que contiene las estructuras, y i es el índice al que se debe acceder a la matriz, entonces el desplazamiento de bytes a cada estructura es i*24.Multiplicar por 24 en este caso se logra mediante una combinación de lea y direccionamiento SIB. La primera instrucción lea simplemente calcula i*3, luego cada instrucción subsiguiente usa esa i*3 y la multiplica aún más por 8, accediendo a la matriz en el offset de bytes absoluto necesario y luego usando desplazamientos inmediatos para acceder a los miembros individuales de la estructura ((%rdi,%rcx,8). 8(%rdi,%rcx,8), y 16(%rdi,%rcx,8)). Si hace que el tamaño de la estructura sea de 88 bytes, simplemente no hay forma de hacerlo rápidamente con una combinación de lea y cualquier tipo de direccionamiento. El compilador simplemente asume que un simple imull será más eficiente en el cálculo de i*88 que una serie de cambios, agrega, lea o cualquier otra cosa.

+0

He publicado el código que obtuve. – Matt

+0

Sí, sé todo eso. Mi pregunta es si vale la pena rellenar la estructura con espacio adicional solo para que sea un número "redondo" (como 12 largos en lugar de 11 largos) lo que evitaría usar una multiplicación al calcular el índice de matriz. – Matt

+1

@Matt: nadie puede responder que, en general, el relleno no viene gratis (tamaños de caché); no adivinen, midan! – Christoph

2
  1. ¿Cuál es el propósito de pushq% RBP, movq% RSP,% RBP, y popq% RBP, si ninguno RBP ni RSP se utiliza en el cuerpo de la función?

Para realizar un seguimiento de los marcos cuando utiliza un depurador. Agregue para optimizar (tenga en cuenta que debe habilitarse en -O3 pero en muchas versiones de gcc que usé no).

0
3. I tried increasing the size of Foo to 88 bytes (11 longs) and the leaq instruction became an imulq. Would it make sense to design my structs to have "rounder" sizes to avoid the multiply instructions (in order to optimize array access)? 

La llamada leaq es (esencialmente y en este CAE) calcular k * A + B donde "k" es 1, 2, 4, u 8 y "a" y "b" son registros . Si "a" y "b" son iguales, se pueden usar para estructuras de 1, 2, 3, 4, 5, 8 y 9 largos.

Las estructuras más grandes como 16 longs pueden ser optimizables calculando el desplazamiento con para "k" y doblar, pero no sé si eso es lo que el compilador realmente hará; tendrías que probar

+0

Lo probé con doce y lo optimizo. ("' leaq (% rsi,% rsi, 2),% rcx' "y luego" 'shlq $ 5,% rcx'") Pero mi pregunta era si vale la pena aumentar el tamaño digamos del 88 al 96 solo para evitar un multiplicar durante el acceso a la matriz (suponiendo que voy a hacer un montón de acceso a la matriz). – Matt

+1

Ah, lo siento. Si la memoria es menos importante que el rendimiento y puede estar seguro de que se evitará el imul, entonces sí, lo haría. (Inserte aquí el descargo de responsabilidad estándar sobre la preoptimización y las pruebas para verificar). – DocMax

Cuestiones relacionadas