En informática , una tubería , también conocida como canalización de datos , es un conjunto de elementos de procesamiento de datos conectados en serie, donde la salida de un elemento es la entrada del siguiente. Los elementos de una tubería suelen ejecutarse en paralelo o en intervalos de tiempo. Generalmente se inserta cierta cantidad de almacenamiento intermedio entre los elementos.
Concepto y motivación
El procesamiento en paralelo es un concepto común en la vida cotidiana. Por ejemplo, en la línea de montaje de una fábrica de automóviles, cada tarea específica —como instalar el motor, el capó o las ruedas— suele realizarse en una estación de trabajo independiente. Las estaciones ejecutan sus tareas en paralelo, cada una en un automóvil diferente. Una vez que se ha completado una tarea en un automóvil, este pasa a la siguiente estación. Las variaciones en el tiempo necesario para completar las tareas se pueden compensar mediante la "reserva" (manteniendo uno o más automóviles en un espacio entre las estaciones) o mediante la "parada" (deteniendo temporalmente las estaciones anteriores) hasta que la siguiente estación esté disponible.
Supongamos que el ensamblaje de un automóvil requiere tres tareas que toman 20, 10 y 15 minutos, respectivamente. Si las tres tareas se realizaran en una sola estación, la fábrica produciría un automóvil cada 45 minutos. Al utilizar una cadena de montaje con tres estaciones, la fábrica produciría el primer automóvil en 45 minutos y, posteriormente, uno nuevo cada 20 minutos.
Como muestra este ejemplo, el procesamiento en paralelo no reduce la latencia , es decir, el tiempo total que tarda un elemento en pasar por todo el sistema. Sin embargo, sí aumenta el rendimiento del sistema , es decir, la velocidad a la que se procesan nuevos elementos después del primero.
En informática

En informática , una tubería o canalización de datos [ 1 ] es un conjunto de elementos de procesamiento de datos conectados en serie, donde la salida de un elemento es la entrada del siguiente. Los elementos de una tubería suelen ejecutarse en paralelo o en intervalos de tiempo. A menudo se inserta cierta cantidad de almacenamiento intermedio entre los elementos.
Las tuberías relacionadas con la informática incluyen:
- Las arquitecturas de procesamiento en paralelo (pipelines ), como la clásica arquitectura RISC , se utilizan en unidades centrales de procesamiento (CPU) y otros microprocesadores para permitir la ejecución simultánea de múltiples instrucciones con el mismo circuito . El circuito se divide generalmente en etapas, y cada etapa procesa una parte específica de una instrucción a la vez, pasando los resultados parciales a la siguiente etapa. Ejemplos de etapas son la decodificación de instrucciones, la aritmética/lógica y la búsqueda de registros. Estas tecnologías están relacionadas con la ejecución superescalar , el reenvío de operandos , la ejecución especulativa y la ejecución fuera de orden .
- Las tuberías gráficas , presentes en la mayoría de las unidades de procesamiento gráfico (GPU), constan de múltiples unidades aritméticas o CPU completas que implementan las distintas etapas de las operaciones de renderizado comunes ( proyección en perspectiva , recorte de ventanas , cálculo de color y luz , renderizado, etc.).
- Las tuberías de software , que consisten en una secuencia de procesos informáticos (comandos, ejecuciones de programas, tareas, subprocesos, procedimientos, etc.), se ejecutan conceptualmente en paralelo, donde el flujo de salida de un proceso se alimenta automáticamente como flujo de entrada del siguiente. La tubería de llamadas al sistema Unix es un ejemplo clásico de este concepto.
- La técnica de canalización HTTP consiste en enviar múltiples solicitudes HTTP a través de la misma conexión TCP , sin esperar a que finalice la anterior antes de enviar una nueva.
Si una canalización consta de pasos idempotentes , estos pueden repetirse sin alterar el resultado más allá de la ejecución inicial. Esto resulta ventajoso en términos de fiabilidad y mantenimiento.
Consideraciones de diseño
Equilibrando las etapas
Dado que el rendimiento de una cadena de producción no puede ser superior al de su elemento más lento, el diseñador debe intentar distribuir el trabajo y los recursos entre las etapas de manera que todas tarden el mismo tiempo en completar sus tareas. En el ejemplo del ensamblaje de automóviles mencionado anteriormente, si las tres tareas tardaran 15 minutos cada una, en lugar de 20, 10 y 15 minutos, la latencia seguiría siendo de 45 minutos, pero se terminaría un automóvil nuevo cada 15 minutos, en lugar de cada 20.
Almacenamiento en búfer
En circunstancias ideales, si todos los elementos de procesamiento están sincronizados y tardan el mismo tiempo en procesarse, cada elemento puede ser recibido por cada elemento en el mismo instante en que el anterior lo libera, en un solo ciclo de reloj . De esta forma, los elementos fluirán a través de la tubería a una velocidad constante, como las olas en un canal de agua. En tales "tuberías de ondas" [ 2 ] , no se requiere sincronización ni almacenamiento en búfer entre las etapas, aparte del almacenamiento necesario para los elementos de datos.
En términos más generales, el almacenamiento temporal entre las etapas del flujo de procesamiento es necesario cuando los tiempos de procesamiento son irregulares o cuando se pueden crear o destruir elementos a lo largo del flujo. Por ejemplo, en un flujo de procesamiento gráfico que procesa triángulos para su visualización en pantalla, un elemento que verifica la visibilidad de cada triángulo puede descartarlo si es invisible o generar dos o más fragmentos triangulares del elemento si están parcialmente ocultos. El almacenamiento temporal también es necesario para compensar las irregularidades en la velocidad a la que la aplicación envía elementos a la primera etapa y consume la salida de la última.
El búfer entre dos etapas puede ser simplemente un registro de hardware con la lógica de sincronización y señalización adecuada. Cuando la etapa A almacena un dato en el registro, envía una señal de "datos disponibles" a la siguiente etapa B. Una vez que B ha utilizado esos datos, responde con una señal de "datos recibidos" a A. La etapa A se detiene, esperando esta señal, antes de almacenar el siguiente dato en el registro. La etapa B se detiene, esperando la señal de "datos disponibles", si está lista para procesar el siguiente dato pero la etapa A aún no lo ha proporcionado.
Si los tiempos de procesamiento de un elemento son variables, es posible que toda la cadena de procesamiento deba detenerse con frecuencia, esperando a que dicho elemento y los anteriores consuman los datos de sus búferes de entrada. La frecuencia de estas paradas puede reducirse proporcionando espacio para más de un elemento en el búfer de entrada de esa etapa. Este búfer de múltiples elementos se suele implementar como una cola FIFO (primero en entrar, primero en salir) . La etapa anterior aún podría tener que detenerse cuando la cola se llene, pero la frecuencia de estos eventos disminuirá a medida que se proporcionen más ranuras de búfer. La teoría de colas permite determinar el número de ranuras de búfer necesarias, en función de la variabilidad de los tiempos de procesamiento y del rendimiento deseado.
Tuberías no lineales
Si alguna etapa tarda (o puede tardar) mucho más que las demás y no se puede acelerar, el diseñador puede proporcionar dos o más elementos de procesamiento para realizar esa tarea en paralelo, con un único búfer de entrada y un único búfer de salida. Cuando cada elemento termina de procesar su dato actual, lo entrega al búfer de salida común y toma el siguiente dato del búfer de entrada común. Este concepto de procesamiento en cadena "no lineal" o "dinámico" se ejemplifica en tiendas o bancos que tienen dos o más cajeros atendiendo a los clientes desde una única cola de espera.
Dependencias entre elementos
En algunas aplicaciones, el procesamiento de un elemento Y por parte de la etapa A puede depender de los resultados o el efecto del procesamiento de un elemento X anterior por parte de una etapa B posterior del proceso. En ese caso, la etapa A no puede procesar correctamente el elemento Y hasta que el elemento X haya finalizado el procesamiento en la etapa B.
Esta situación se presenta con frecuencia en las tuberías de instrucciones. Por ejemplo, supongamos que Y es una instrucción aritmética que lee el contenido de un registro que debería haber sido modificado por una instrucción X anterior. Sea A la etapa que obtiene los operandos de la instrucción y B la etapa que escribe el resultado en el registro especificado. Si la etapa A intenta procesar la instrucción Y antes de que la instrucción X llegue a la etapa B, el registro aún podría contener el valor anterior, y el efecto de Y sería incorrecto.
Para gestionar correctamente este tipo de conflictos, la tubería debe contar con circuitos o lógica adicionales que los detecten y tomen las medidas adecuadas. Algunas estrategias para lograrlo incluyen:
- Retraso: Cada etapa afectada, como la A, se detiene hasta que se resuelve la dependencia, es decir, hasta que se dispone de la información necesaria o se alcanza el estado requerido.
- Reordenamiento de elementos: En lugar de detenerse, la etapa A puede dejar de lado el elemento Y y buscar cualquier elemento Z subsiguiente en su flujo de entrada que no tenga dependencias pendientes con ningún elemento anterior. En las tuberías de instrucciones, esta técnica se denomina ejecución fuera de orden .
- Adivinar y retroceder: Un ejemplo importante de dependencia entre elementos es el manejo de una instrucción de salto condicional X por parte de una tubería de instrucciones. La primera etapa A de la tubería, que obtiene la siguiente instrucción Y que se ejecutará, no puede realizar su tarea hasta que X haya obtenido su operando y haya determinado si el salto debe tomarse o no. Esto puede llevar muchos ciclos de reloj, ya que el operando de X puede, a su vez, depender de instrucciones anteriores que obtienen datos de la memoria principal.
- En lugar de detenerse mientras espera a que X termine, la etapa A puede adivinar si se tomará la bifurcación o no, y obtener la siguiente instrucción Y basándose en esa adivinación. Si la adivinación resulta ser incorrecta (esperemos que rara vez), el sistema tendría que retroceder y reanudar con la opción correcta. Es decir, todos los cambios realizados en el estado de la máquina por la etapa A y las etapas subsiguientes basándose en esa adivinación tendrían que deshacerse, las instrucciones que siguen a X que ya están en la tubería tendrían que vaciarse, y la etapa A tendría que reiniciarse con el puntero de instrucción correcto . Esta estrategia de predicción de bifurcaciones es un caso especial de ejecución especulativa .
Implementaciones de software típicas
Para su correcta implementación, las canalizaciones de datos requieren una estrategia de planificación de CPU que distribuya el trabajo entre los núcleos disponibles, así como el uso de estructuras de datos sobre las que operarán las distintas etapas de la canalización. Por ejemplo, los sistemas derivados de UNIX pueden canalizar comandos que conectan las operaciones de entrada/salida estándar de varios procesos, utilizando las tuberías implementadas por el sistema operativo. Algunos sistemas operativos ofrecen una sintaxis similar a la de UNIX para encadenar varias ejecuciones de programas en una canalización, pero la implementan como una simple ejecución en serie, en lugar de una verdadera canalización, es decir, esperando a que cada programa finalice antes de iniciar el siguiente.
Los enfoques de nivel inferior pueden basarse en los subprocesos proporcionados por el sistema operativo para programar el trabajo en las etapas: tanto las implementaciones basadas en grupos de subprocesos como las de un subproceso por etapa son viables y existen. [ 3 ]
Existen otras estrategias basadas en la multitarea cooperativa que no requieren múltiples hilos de ejecución ni, por lo tanto, núcleos de CPU adicionales, como el uso de un planificador round-robin con un marco de trabajo basado en corrutinas. En este contexto, cada etapa puede instanciarse con su propia corrutina, devolviendo el control al planificador una vez finalizada su tarea. Este enfoque puede requerir un control preciso de las etapas del proceso para evitar que abusen de su tiempo de ejecución.
Costos e inconvenientes
Un sistema en paralelo generalmente requiere más recursos (elementos de circuito, unidades de procesamiento, memoria de computadora, etc.) que uno que ejecuta un lote a la vez, porque sus etapas no pueden compartir esos recursos y porque puede ser necesario el almacenamiento en búfer y lógica de sincronización adicional entre los elementos.
Además, la transferencia de elementos entre distintos componentes de procesamiento puede aumentar la latencia, especialmente en el caso de flujos de datos largos.
El coste adicional de complejidad que supone la segmentación puede ser considerable si existen dependencias entre el procesamiento de diferentes elementos, especialmente si se utiliza una estrategia de prueba y error para gestionarlas. De hecho, el coste de implementar dicha estrategia para conjuntos de instrucciones complejos ha motivado algunas propuestas radicales para simplificar la arquitectura de los ordenadores , como RISC y VLIW . Los compiladores también se han visto obligados a reorganizar las instrucciones de máquina para mejorar el rendimiento de las segmentaciones de instrucciones.
Nuevas tecnologías
En los últimos años, las exigencias sobre las aplicaciones y su hardware subyacente han sido significativas. Por ejemplo, construir pipelines con aplicaciones de un solo nodo que recorren los datos fila por fila ya no es factible con el volumen y la variedad de big data . Sin embargo, con la llegada de motores de análisis de datos como Hadoop , o más recientemente Apache Spark , ha sido posible distribuir grandes conjuntos de datos entre múltiples nodos de procesamiento, lo que permite que las aplicaciones alcancen niveles de eficiencia cientos de veces superiores a los que se creían posibles anteriormente. El efecto de esto es que hoy en día incluso un PC de gama media que utiliza procesamiento distribuido de esta manera puede gestionar la construcción y ejecución de pipelines de big data. [ 4 ]
Véase también
Referencias
- ↑ Desarrollo de canalizaciones de datos Archivado el 24/05/2018 en Wayback Machine Publicado por Dativa, recuperado el 24 de mayo de 2018
- ↑ O. Hauck; Sorin A. Huss; M. Garg (1999). "Pipelines de onda asíncronos de dos fases y su aplicación a una DCT 2D". Actas del Quinto Simposio Internacional sobre Investigación Avanzada en Circuitos y Sistemas Asíncronos . págs. 219–228 . doi : 10.1109/ASYNC.1999.761536 . ISBN 0-7695-0031-5. S2CID 206515615 .
- ↑ "MTDP" . GitHub . Septiembre de 2022.
- ↑ ¿Qué es un flujo de datos? Publicado por Data Pipelines, consultado el 11 de marzo de 2021.
Bibliografía
- Pérez García, Pablo (2018). Pipeline DSL: un DSL para crear un pipeline de CI/CD para tus proyectos . Addison-Wesley. ISBN 978-0-134-69147-3.
- Para una discusión estándar sobre la segmentación en computación paralela, véase Quinn, Michael J. (2004). Parallel Programming in C with MPI and openMP . Dubuque, Iowa: McGraw-Hill Professional. ISBN 0072822562.
- Pogonyi, Roland (febrero de 2021). "¿Qué es una canalización de datos?" . Recuperado el 11 de marzo de 2021 .
- Procesamiento de instrucciones