Pregunta

Así, super bajo nivel lo que hace un SI () verá así, ¿cómo es manejado por un procesador x86?

¿Fue útil?

Solución

El procesador tiene "Branch si" instrucciones que cuando se cumple que las ramas una cierta condición, y de lo contrario continúa a la siguiente instrucción.

Entonces

if(A)
{
    dosomething;
}

se convertiría

load A into register 0
if the zero flag is set (ie, register 0 contains 0x00) then jump to endcondition)
dosomething
endcondition:

condiciones más complejas (if(A || B && C)) se convierten en una secuencia de instrucciones que deja un registro en un estado 0 o distinto de cero, por lo que la instrucción branchif puede saltar o no saltar sobre la base de las banderas condicionales.

Hay muchas banderas condicionales (cero, llevan, negativo, desbordamiento, etc.), y algunas instrucciones branchif también operan en condiciones más complejas (es decir, en realidad podría comprobar para ver si un registro es igual a otro registro, en lugar de simplemente mirando las banderas). Cada arquitectura es diferente y hace que las compensaciones por lo que el conjunto de instrucciones es completa, sino también rápido y compacto.

Como moocha señala en los comentarios, algunas arquitecturas permitirá aplicar un condicionales para algunos, muchos, o incluso todas las instrucciones, lo que no sólo podría tener 'sucursal si' instrucciones, sino también 'y si', 'añadir si', 'moverse si', etc.

El x86 es muy, muy, muy complejo más allá de esta explicación simple una vez llegue a la canalización, ejecución fuera de orden, el almacenamiento en caché, microcódigo, y todos los otros temas avanzados. Para la mayoría de los propósitos de la explicación anterior es suficiente. Si usted está escribiendo una mano hecha a mano muy, muy bien la herida algoritmo, sin embargo, usted tiene que tomar estas cosas en cuenta para el máximo rendimiento y la productividad.

Eso es un tema para otra pregunta, sin embargo ...

-Adán

Otros consejos

Es bastante fácil de usar la salida de un compilador de C (utilizar el interruptor -S en gcc) para ver qué salida de un fragmento dado de C generará cuando se compila. Tenga cuidado al utilizar la optimización de los programas de juguete sin embargo. Si usted no tiene cuidado el optimizador menudo optimizará distancia condicionales que siempre va a ir una manera u otra (ver este artículo en microbenchmarks para una explicación más detallada).

Por ejemplo, un programa trivial C:

#include <stdio.h>

int main (int argc, char **argv) {
    int ii = 10;
    int jj = 20;
    if (jj > ii) {
        puts ("jj > ii \n");
    }
    return 0;
}

compila a la siguiente lenguaje ensamblador:

    .file   "foo.c"
    .section    .rodata
.LC0:
    .string "jj > ii \n"
    .text
.globl main
    .type   main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $20, %esp
    movl    $10, -8(%ebp)
    movl    $20, -12(%ebp)
    movl    -12(%ebp), %eax
    cmpl    -8(%ebp), %eax
    jle .L2
    movl    $.LC0, (%esp)
    call    puts
.L2:
    movl    $0, %eax
    addl    $20, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.3.2-1ubuntu12) 4.3.2"
    .section    .note.GNU-stack,"",@progbits

Durante un breve disección de lo que está pasando:

  • La primera sección (.rodata) declara una constante con la cadena 'jj > ii \n')

  • La segunda sección está inicializar el contenido de las variables ii y jj en la pila.

  • El bit de cmpl -8(%ebp), %eax está haciendo la comparación real; la instrucción jle está saltando sobre la llamada a 'puts', que es efectivamente la lógica de la declaración 'if' invertida.

  • Después de la etiqueta '.L2' el sistema está ordenando la parte superior de la pila y volver de la llamada.

Es una instrucción de salto, dependiente de la arquitectura específica de la máquina. Se da cuenta de cómo configurar una ubicación de memoria o registro para comprobar si una condición específica de bajo nivel - como filial, si-no-iguales o rama-si-no-cero, ... - ¿Eso prueba luego salta ( o no si la condición no) lo hace a otra parte de la memoria. Obviamente, si usted tiene una condición compleja que puede necesitar hacer evaluar muchas condiciones diferentes y puede implicar varias instrucciones de bifurcación.

En general, la CPU tiene lo que se llama un registro de instrucción, que contiene la dirección de memoria del código de operación actual lenguaje de máquina para ser ejecutado siguiente ... y muchos otros registros para almacenar los datos.

Generalmente, después de la CPU ejecuta cada código de operación en el registro de la instrucción, simplemente se incrementa por uno para mover a la siguiente posición en la memoria que debería tener el siguiente código de operación en la aplicación de programa compilado.

Un código de operación (en realidad es probable que haya varios), sin embargo permite que la CPU "Rama", por "comparar" los valores de otras dos registros de la CPU, y si uno es mayor que el otro, se copia a una dirección de memoria en la registro de instrucción, mientras que si el otro es el más grande, se copia a una segunda dirección, memoria diferente en el registro de instrucción.

Eso es lo más nivel "bajo", ya que puede ser puesto que w / o hablando de relés y transistores ...

Aquí hay una muy buena visión general de cómo tales estructuras podrían recopilar en la arquitectura x86: http://en.wikibooks.org/wiki/X86_Disassembly/Branches#If-Then

Hay maneras de evitar a veces una rama (que a menudo tiene consecuencias en el rendimiento fuertemente negativas debido a la tubería de ruptura). Por ejemplo la instrucción i686 establecer en adelante (todo, desde Pentium Pro para el día actual) tiene una instrucción de movimiento condicional que podría compilar este:

if (a==0) {
    b= 1;
}

a algo como esto:

cmp    0, eax
cmovzl ebx, 1

con ninguna rama, siempre y cuando el compilador está configurado para apuntar i686 + (y se siente como que; compiladores son complejos e inescrutable). SET [estado] es otra, instrucciones similares, condicional.

Lucky viejos programadores ARM llegar a especificar cualquier instrucción condicional, lo que reduce las ramas mucho.

A pesar de que la mayoría de las sentencias if va a terminar siendo ramas condicionales, para casos muy simples donde ni la rama tiene ningún efecto secundario, un compilador de optimización puede generar código que se ejecuta tanto y calcula el resultado y no sólo la ejecución de uno. Esto puede tener ventajas en las arquitecturas pipeline donde el coste medio de cálculo de ambas ramas es menor que el costo promedio debido a fallos de predicción de decisiones.

Por ejemplo, el código:

int x;

if ( y < 5 )
  x = 5;
else
  x = y;

puede ser compilado como si estuviera escrito:

y -= 5
int r = y < 0; // r is 1 if y < 5, 0 otherwise
r -= 1         // r is 0x00000000 if y < 5, 0xffffffff otherwise
x = y & r      // x is 0 if y < 5, (y-5) otherwise 
x += 5;        // x is 5 if y < 5, y otherwise

que puede ser convertido en código de máquina, sin ramificaciones

Básicamente, usted tiene un montón de electrones que se pasa alrededor de entre varios átomos dentro de la CPU. Debido a la estructura de los átomos de silicio en su CPU, los electrones siguen ciertos caminos, que determinan la rama de ejecución seguirá el ordenador.

EDIT: Parece que debería explicar un poco menos vagamente . Tengan paciencia conmigo, que se especializó en ciencias de la computación, no ingeniería eléctrica, por lo que no tienen un conocimiento muy profundo de estas cosas:

Su CPU está hecha de un material, generalmente de silicio, que se llama un "semiconductor". Una de las grandes cosas acerca de los semiconductores es que sus propiedades elecrical se puede variar fácilmente a través de "dopaje", o la aplicación de impurezas que crean áreas de "portadores de carga" negativos o positivos en el material. Las líneas en las que estas áreas se unen son conocidos como "uniones", y la electricidad fluye mucho más fácilmente un camino a través de estas uniones que el otro. Esta propiedad se aprovecha para crear diodos, que permiten que la electricidad fluya en una sola dirección, y transistores, que pueden ser considerados como pequeños interruptores que permiten que una corriente eléctrica para controlar a otra corriente eléctrica. Estos transistores y diodos se combinan en una miríada de maneras de crear las puertas lógicas de la CPU.

Muchas de las puertas lógicas dentro de la CPU están dedicados a ser la "unidad de control", que se encarga de recuperar e instrucciones de descodificación, diciendo al resto de la CPU qué hacer, y, finalmente, conseguir la siguiente instrucción. En el 86, la unidad de control está funcionando realmente "microcódigo", que le diga cómo hacer frente a la ramificación, la canalización, y así sucesivamente. Que realmente necesita para ser muy específico acerca de una determinada línea de procesadores para entrar en la forma en la ISA x86 se implementa en una microarquitectura particular.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top