En ingeniería informática , la canalización de instrucciones es una técnica para implementar el paralelismo a nivel de instrucción dentro de un solo procesador. La canalización intenta mantener cada parte del procesador ocupada con alguna instrucción dividiendo las instrucciones entrantes en una serie de pasos secuenciales (el epónimo " canalización ") realizados por diferentes unidades de procesador con diferentes partes de instrucciones procesadas en paralelo.
En una computadora segmentada, las instrucciones fluyen a través de la unidad central de procesamiento (CPU) en etapas. Por ejemplo, podría tener una etapa para cada paso del ciclo de von Neumann : obtener la instrucción, obtener los operandos, ejecutar la instrucción, escribir los resultados. Una computadora segmentada generalmente tiene "registros de segmentación" después de cada etapa. Estos almacenan información de la instrucción y los cálculos para que las puertas lógicas de la siguiente etapa puedan realizar el siguiente paso.
Esta disposición permite que la CPU complete una instrucción en cada ciclo de reloj. Es común que las etapas pares operen en un borde del reloj de onda cuadrada, mientras que las etapas impares operan en el otro borde. Esto permite un mayor rendimiento de la CPU que una computadora multiciclo a una frecuencia de reloj dada , pero puede aumentar la latencia debido a la sobrecarga adicional del proceso de segmentación en sí. Además, aunque la lógica electrónica tiene una velocidad máxima fija, una computadora segmentada puede hacerse más rápida o más lenta variando el número de etapas en la segmentación. Con más etapas, cada etapa hace menos trabajo y, por lo tanto, la etapa tiene menos demoras de las puertas lógicas y podría funcionar a una frecuencia de reloj más alta.
Un modelo de computadora segmentada suele ser el más económico, cuando el costo se mide en puertas lógicas por instrucción por segundo. En cada instante, una instrucción está en una sola etapa de segmentación y, en promedio, una etapa de segmentación es menos costosa que una computadora multiciclo. Además, cuando está bien hecha, la mayor parte de la lógica de la computadora segmentada está en uso la mayor parte del tiempo. En contraste, las computadoras fuera de servicio generalmente tienen grandes cantidades de lógica inactiva en un instante dado. Cálculos similares generalmente muestran que una computadora segmentada usa menos energía por instrucción.
Sin embargo, una computadora segmentada suele ser más compleja y más costosa que una computadora multiciclo comparable. Normalmente tiene más puertas lógicas, registros y una unidad de control más compleja. De manera similar, puede utilizar más energía total, mientras que utiliza menos energía por instrucción. Las CPU fuera de servicio generalmente pueden realizar más instrucciones por segundo porque pueden realizar varias instrucciones a la vez.
En una computadora segmentada, la unidad de control organiza el inicio, la continuación y la detención del flujo según las órdenes del programa. Los datos de instrucción se pasan generalmente en registros segmentados de una etapa a la siguiente, con una parte de lógica de control algo separada para cada etapa. La unidad de control también garantiza que la instrucción en cada etapa no perjudique el funcionamiento de las instrucciones en otras etapas. Por ejemplo, si dos etapas deben utilizar la misma parte de datos, la lógica de control garantiza que los usos se realicen en la secuencia correcta.
Cuando funciona de manera eficiente, una computadora segmentada tendrá una instrucción en cada etapa. Luego trabaja en todas esas instrucciones al mismo tiempo. Puede terminar aproximadamente una instrucción por cada ciclo de su reloj. Pero cuando un programa cambia a una secuencia de instrucciones diferente, la segmentación a veces debe descartar los datos en proceso y reiniciarse. Esto se llama "bloqueo".
Gran parte del diseño de una computadora canalizada evita interferencias entre las etapas y reduce los bloqueos.
La cantidad de pasos dependientes varía según la arquitectura de la máquina. Por ejemplo:
A medida que el pipeline se hace más "profundo" (con un mayor número de pasos dependientes), un paso determinado se puede implementar con circuitos más simples, lo que puede permitir que el reloj del procesador funcione más rápido. [3] Tales pipelines pueden llamarse superpipelines. [4]
Se dice que un procesador está completamente segmentado si puede obtener una instrucción en cada ciclo. Por lo tanto, si algunas instrucciones o condiciones requieren demoras que inhiben la obtención de nuevas instrucciones, el procesador no está completamente segmentado.
Los usos fundamentales del pipeline fueron en el proyecto ILLIAC II y el proyecto IBM Stretch , aunque una versión simple se utilizó antes en el Z1 en 1939 y el Z3 en 1941. [5]
El pipelining comenzó a utilizarse en serio a finales de los años 1970 en supercomputadoras como los procesadores vectoriales y los procesadores de matriz. [ cita requerida ] Una de las primeras supercomputadoras fue la serie Cyber construida por Control Data Corporation. Su arquitecto principal, Seymour Cray , más tarde dirigió Cray Research. Cray desarrolló la línea de supercomputadoras XMP, utilizando el pipelining tanto para funciones de multiplicación como de suma/resta. Más tarde, Star Technologies añadió el paralelismo (varias funciones pipelined trabajando en paralelo), desarrollado por Roger Chen. En 1984, Star Technologies añadió el circuito divisor pipelined desarrollado por James Bradley. A mediados de los años 1980, el pipelining ya lo utilizaban muchas empresas diferentes en todo el mundo. [ cita requerida ]
La segmentación no se limitaba a las supercomputadoras. En 1976, el mainframe de propósito general de la serie 470 de Amdahl Corporation tenía una segmentación de siete pasos y un circuito de predicción de ramificaciones patentado. [ cita requerida ]
El modelo de ejecución secuencial supone que cada instrucción se completa antes de que comience la siguiente; esta suposición no es cierta en un procesador segmentado. Una situación en la que el resultado esperado es problemático se conoce como peligro . Imagine las siguientes dos instrucciones de registro para un procesador hipotético:
1: suma 1 a R52: copiar R5 a R6
Si el procesador tiene los 5 pasos enumerados en la ilustración inicial (la 'tubería básica de cinco etapas' al comienzo del artículo), la instrucción 1 se obtendría en el tiempo t 1 y su ejecución se completaría en t 5. La instrucción 2 se obtendría en t 2 y se completaría en t 6. La primera instrucción podría depositar el número incrementado en R5 como su quinto paso (reescritura del registro) en t 5. Pero la segunda instrucción podría obtener el número de R5 (para copiarlo a R6) en su segundo paso (decodificación de la instrucción y obtención del registro) en el tiempo t 3. Parece que la primera instrucción no habría incrementado el valor para entonces. El código anterior invoca un peligro.
Escribir programas de computadora en un lenguaje compilado podría no plantear estos problemas, ya que el compilador podría estar diseñado para generar código de máquina que evite riesgos.
En algunos procesadores DSP y RISC tempranos, la documentación aconseja a los programadores evitar tales dependencias en instrucciones adyacentes y casi adyacentes (llamadas ranuras de retardo ), o declara que la segunda instrucción usa un valor antiguo en lugar del valor deseado (en el ejemplo anterior, el procesador podría copiar de manera contraria a la intuición el valor no incrementado), o declara que el valor que usa no está definido. El programador puede tener trabajo no relacionado que el procesador puede hacer mientras tanto; o, para asegurar resultados correctos, el programador puede insertar NOP en el código, anulando en parte las ventajas de la segmentación.
Los procesadores segmentados suelen utilizar tres técnicas para funcionar como se espera cuando el programador supone que cada instrucción se completa antes de que comience la siguiente:
Salirse de la secuencia normal de instrucciones suele implicar un riesgo. A menos que el procesador pueda hacer efectiva la ramificación en un solo ciclo de tiempo, la secuencia seguirá obteniendo instrucciones de forma secuencial. No se puede permitir que dichas instrucciones surtan efecto porque el programador ha desviado el control a otra parte del programa.
Una bifurcación condicional es aún más problemática. El procesador puede o no bifurcarse, dependiendo de un cálculo que aún no se ha realizado. Varios procesadores pueden detenerse, pueden intentar la predicción de bifurcaciones y pueden comenzar a ejecutar dos secuencias de programas diferentes ( ejecución ansiosa ), cada una asumiendo que la bifurcación se realiza o no, descartando todo el trabajo que pertenece a la suposición incorrecta. [a]
Un procesador con una implementación de predicción de bifurcaciones que generalmente realiza predicciones correctas puede minimizar la pérdida de rendimiento que se produce por la bifurcación. Sin embargo, si las bifurcaciones se predicen de manera deficiente, puede generar más trabajo para el procesador, como eliminar de la secuencia de comandos la ruta de código incorrecta que ha comenzado la ejecución antes de reanudarla en la ubicación correcta.
Los programas escritos para un procesador segmentado evitan deliberadamente la ramificación para minimizar la posible pérdida de velocidad. Por ejemplo, el programador puede manejar el caso habitual con ejecución secuencial y ramificar solo al detectar casos inusuales. El uso de programas como gcov para analizar la cobertura del código permite al programador medir la frecuencia con la que se ejecutan realmente determinadas ramificaciones y obtener información con la que optimizar el código. En algunos casos, un programador puede manejar tanto el caso habitual como el caso inusual con código sin ramificaciones .
A la derecha hay una secuencia genérica con cuatro etapas: obtención, decodificación, ejecución y escritura diferida. El cuadro gris superior es la lista de instrucciones que esperan ser ejecutadas, el cuadro gris inferior es la lista de instrucciones que ya se han ejecutado y el cuadro blanco del medio es la secuencia.
La ejecución es la siguiente:
Un procesador canalizado puede lidiar con los riesgos deteniéndose y creando una burbuja en el canal, lo que da como resultado uno o más ciclos en los que no sucede nada útil.
En la ilustración de la derecha, en el ciclo 3, el procesador no puede decodificar la instrucción violeta, tal vez porque determina que la decodificación depende de los resultados producidos por la ejecución de la instrucción verde. La instrucción verde puede pasar a la etapa de ejecución y luego a la etapa de escritura diferida según lo programado, pero la instrucción violeta se detiene durante un ciclo en la etapa de obtención. La instrucción azul, que debía obtenerse durante el ciclo 3, se detiene durante un ciclo, al igual que la instrucción roja que le sigue.
Debido a la burbuja (los óvalos azules en la ilustración), el circuito de decodificación del procesador está inactivo durante el ciclo 3. Su circuito de ejecución está inactivo durante el ciclo 4 y su circuito de escritura diferida está inactivo durante el ciclo 5.
Cuando la burbuja sale de la tubería (en el ciclo 6), se reanuda la ejecución normal. Pero ahora todo tiene un ciclo de retraso. Se necesitarán 8 ciclos (del ciclo 1 al 8) en lugar de 7 para ejecutar por completo las cuatro instrucciones que se muestran en colores. [b]