¿Cómo ejecutan las líneas de código la CPU?

11

Estoy tratando de entender realmente cómo se convierte exactamente un lenguaje de alto nivel en código de máquina y luego se ejecuta por la CPU.

Entiendo que el código está compilado en código de máquina, que es el código de bajo nivel que puede usar una CPU. Si tengo una declaración de asignación, diga:

x = x + 5;
y = x - 3;

¿La CPU ejecuta cada línea una a la vez? Por lo tanto, primero ejecutará x = x + 5; instrucción y luego la siguiente instrucción que ejecutará la CPU es y = x- 3; Realmente estoy tratando de entender el proceso de ejecución y cómo el código que escribo es realmente ejecutado por la CPU.

Frankie
fuente
Es posible que desee intentar comprender el diseño de una de las CPU de código abierto, hay algunas implementaciones realmente simples basadas en pila como excamera.com/sphinx/fpga-j1.html : son mucho más simples que las arquitecturas de 3 direcciones como en tu ejemplo.
SK-logic
3
Cuando entré en este negocio, habría tenido respuestas simples y bien definidas. Hoy en día, las CPU son extremadamente complicadas y hacen todo tipo de cosas para aumentar la potencia de procesamiento.
David Thornley

Respuestas:

12

Las líneas de código no tienen nada que ver con cómo lo ejecuta la CPU. Recomiendo leer sobre ensamblador, porque eso le enseñará mucho sobre cómo el hardware realmente hace las cosas. También puede obtener la salida del ensamblador de muchos compiladores.

Ese código podría compilarse en algo como (en un lenguaje ensamblador inventado):

load R1, [x] ; meaning load the data stored at memory location x into register 1
add R1, 5
store [x], R1 ; store the modified value into the memory location x
sub R1, 3
store R1, [y]

Sin embargo, si el compilador sabe que una variable no se usa nuevamente, es posible que no se emita la operación de almacenamiento.

Ahora, para que el depurador sepa qué código de máquina corresponde a una línea de fuente de programa, el compilador agrega anotaciones para mostrar qué línea corresponde a qué parte del código de máquina.

maxpolun
fuente
Por qué no? Una arquitectura de 3 direcciones tendrá instrucciones como ADD Rx, Rx, $5y SUB Ry, Rx, $3(suponiendo que las variables x e y hayan sido mapeadas en registros). Está describiendo un enfoque RISC de carga / almacenamiento.
SK-logic
1
@ SK-logic: Si bien eso puede suceder para líneas de código muy simples en lenguajes de programación muy simples con tipos de datos y operaciones que la CPU admite bastante bien, no es el caso general. Es conveniente para los expertos, pero primero es importante darse cuenta de que las instrucciones del código de máquina generalmente tienen poca semejanza con las líneas de código en un idioma de alto nivel.
@ SK-Logic: eso solo funciona para este ejemplo en particular. En general, sin embargo, maxpolun tiene razón. Las declaraciones de lenguaje de alto nivel deben traducirse a un lenguaje de nivel inferior, con más "burocracia" necesaria para hacer cosas conceptualmente simples. Supongo que el OP estaba pidiendo un ejemplo de esta transformación.
Andres F.
1
@ SK-Logic: el OP comenzó su pregunta con "Estoy tratando de entender realmente cómo es exactamente un lenguaje de alto nivel" [...]
Andres F.
1
@ SK-logic El contexto es "Si tengo una instrucción de asignación, diga: [fragmento de código] ¿La CPU ejecuta cada línea de una en una?" - Me parece que está destinado a ser el código fuente en un lenguaje que no sea ensamblador. En términos más generales, no veo ningún indicador de una comprensión de cómo es el código de máquina de bajo nivel, y algunas frases (como hablar de líneas) indican algunas ideas falsas. Eso no es tan imposible como implica, no todos tuvieron el placer de ser lanzados de cabeza a algunos microcontroladores simples (como yo y aparentemente otros). Quizás Frankie debería aclarar.
2

Depende.

En los primeros días de máquinas realmente simples, sí, el código ejecutaba una línea a la vez. A medida que las máquinas se hicieron más grandes, más rápidas y más complejas, comenzó a ver tanto la capacidad de ejecutar múltiples instrucciones simultáneamente como las lecturas y escrituras de memoria que tomaban mucho más tiempo que las operaciones en los registros.

Los compiladores de optimización tenían que tener esto en cuenta, y las líneas que proporcionas podían ejecutarse "más o menos" en paralelo, con una parte del procesador trabajando en el cálculo de y, mientras que otra parte almacenaba el nuevo valor previamente calculado de x (y el cálculo de y estaba usando ese nuevo valor del registro).

El Control Data 6600 fue la primera máquina que conozco que hizo este tipo de cosas. La suma de enteros tomó 300 nseg, la referencia de memoria (lectura o escritura) tomó 1000 nseg, las multiplicaciones y las divisiones tomaron MUCHO más tiempo. Se podrían ejecutar hasta diez instrucciones en paralelo, dependiendo de las unidades funcionales que se requirieran. Los compiladores CDC 6600 FORTRAN fueron MUY buenos para programar todo esto.

John R. Strohm
fuente
En este caso, la entrada de la siguiente instrucción depende del resultado de la primera instrucción, por lo que debe ejecutarse secuencialmente.
SK-logic
@ SK-logic: No del todo. La entrada de la segunda línea depende del resultado del lado derecho de la primera línea, pero, basándose únicamente en lo que podemos ver en el código de ejemplo original, puede NO depender de la memoria almacenada del resultado de La primera línea. Si x se hubiera declarado volátil (en C / C ++), entonces se requeriría que el compilador almacenara el resultado primero, Y LUEGO LO RECARGUE DE LA MEMORIA, antes de comenzar a calcular el nuevo valor de y, ya que "volátil" significa que algo (un controlador de interrupciones, por ejemplo) podría entrar y zap x entre las dos líneas.
John R. Strohm
Supuse que x e y son registros (y el código está en un lenguaje de pseudoensamblaje de 3 direcciones en lugar de algo como C). En este caso, ambas instrucciones son inevitablemente secuenciales. De lo contrario, OP tuvo que hacer dos o más preguntas diferentes en lugar de esta.
SK-logic
Me pregunto si los procesadores tratarían de "especular" lo que el valor de xes? De esta manera, ya ha ejecutado el código y lo tiene almacenado en caché.
Kolob Canyon
Incluso si son registros, DEPENDIENDO DE LA MÁQUINA, no puede asumir que las instrucciones se ejecutan de forma completamente secuencial. El 6600 tenía una lógica de programación (el "marcador") que forzaría la semántica secuencial, basada en la suposición de que el programador quería hacer lo obvio. Las máquinas posteriores omitieron ese hardware, confiando en los compiladores para programar las instrucciones cuidadosamente. Los programadores humanos que realizaban programación en lenguaje ensamblador sobre esas bestias eran DE SU PROPIA
John R. Strohm el
1

No, no existe un mapeo uno a uno entre líneas de código / instrucciones en idiomas de nivel superior e inferior. De hecho, ambas líneas anteriores se traducen en múltiples instrucciones de código de máquina , como

  1. cargar un valor de una determinada dirección de memoria en un registro
  2. modificar el valor
  3. escríbelo de nuevo en la memoria

Los detalles reales de estas instrucciones varían entre plataformas.

Esta es la visión básica de las cosas. Sin embargo, para complicar aún más los problemas, las CPU modernas aplican técnicas como tuberías de ejecución , ejecución fuera de orden y múltiples núcleos , entre otros. Esto hace que la CPU haga varias cosas a la vez, por ejemplo, las tuberías procesan diferentes fases de instrucciones posteriores en paralelo dentro de la misma unidad de procesamiento, mientras que múltiples núcleos pueden procesar instrucciones independientes en paralelo.

Péter Török
fuente
0

Debería buscar grandes detalles en un libro para encontrar más detalles sobre cómo funciona, posiblemente también una clase de compilador.

Básicamente, su pregunta se centra en 2 aspectos diferentes.

1) ¿Cómo se traduce el código en código de máquina?

2) ¿Cuándo / cómo se calcula el código usando la paralelización?

La respuesta a 1) depende del idioma que use (aunque para su ejemplo es trivial, por lo que la salida sería la misma). La forma en que el compilador hace la traducción al código de máquina es una de las fuerzas del lenguaje. Además, hay varias preocupaciones que deben tenerse en cuenta en su ejemplo, el código debe cargar los datos en la memoria, almacenarlos, etc.

Finalmente, la paralelización es una característica que puede forzar desde el punto de vista de la programación, pero en pocas palabras, algunos procesadores pueden tratar de pensar que se puede ejecutar una parte del código al mismo tiempo, porque son independientes. En su caso, claramente, no es el caso, ya que necesita ejecutar las declaraciones secuencialmente, por lo que no, no se ejecutará al mismo tiempo.

SRKX
fuente