Son dos frases que describen lo mismo desde puntos de vista (muy ligeramente) diferentes. La programación paralela describe la situación desde el punto de vista del hardware: hay al menos dos procesadores (posiblemente dentro de un solo paquete físico) trabajando en un problema en paralelo. La programación concurrente es describir las cosas más desde el punto de vista del software: dos o más acciones pueden suceder exactamente al mismo tiempo (simultáneamente).
El problema aquí es que las personas están tratando de usar las dos frases para establecer una distinción clara cuando realmente no existe ninguna. La realidad es que la línea divisoria que intentan dibujar ha sido difusa e indistinta durante décadas, y se ha vuelto cada vez más indistinta con el tiempo.
Lo que intentan discutir es el hecho de que una vez, la mayoría de las computadoras tenían una sola CPU. Cuando ejecutó múltiples procesos (o subprocesos) en esa única CPU, la CPU solo estaba ejecutando realmente una instrucción de uno de esos subprocesos a la vez. La aparición de concurrencia fue una ilusión: la CPU cambió entre ejecutar instrucciones de diferentes subprocesos lo suficientemente rápido como para que la percepción humana (a la que algo menos de 100 ms parece instantáneo) parecía que estaba haciendo muchas cosas a la vez.
El contraste obvio de esto es una computadora con múltiples CPU, o una CPU con múltiples núcleos, por lo que la máquina está ejecutando instrucciones de múltiples hilos y / o procesos al mismo tiempo; la ejecución de código uno no puede / no tiene ningún efecto en la ejecución de código en el otro.
Ahora el problema: una distinción tan limpia casi nunca ha existido. Los diseñadores de computadoras en realidad son bastante inteligentes, por lo que notaron hace mucho tiempo que (por ejemplo) cuando necesitabas leer algunos datos de un dispositivo de E / S como un disco, tomaba mucho tiempo (en términos de ciclos de CPU) terminar. En lugar de dejar la CPU inactiva mientras sucedía eso, descubrieron varias formas de permitir que un proceso / hilo realice una solicitud de E / S, y dejar que el código de algún otro proceso / hilo se ejecute en la CPU mientras se completa la solicitud de E / S.
Entonces, mucho antes de que las CPU de varios núcleos se convirtieran en la norma, tuvimos operaciones de múltiples subprocesos que ocurrieron en paralelo.
Sin embargo, esa es solo la punta del iceberg. Hace décadas, las computadoras comenzaron a proporcionar otro nivel de paralelismo también. Nuevamente, siendo personas bastante inteligentes, los diseñadores de computadoras notaron que en muchos casos, tenían instrucciones que no se afectaban entre sí, por lo que era posible ejecutar más de una instrucción desde la misma secuencia al mismo tiempo. Un primer ejemplo que se hizo bastante conocido fue el Control Data 6600. Esta fue (por un margen bastante amplio) la computadora más rápida del mundo cuando se introdujo en 1964, y gran parte de la misma arquitectura básica sigue en uso hoy en día. Rastreaba los recursos utilizados por cada instrucción y tenía un conjunto de unidades de ejecución que ejecutaban instrucciones tan pronto como los recursos de los que dependían estaban disponibles, muy similar al diseño de los procesadores Intel / AMD más recientes.
Pero (como solían decir los comerciales) espera, eso no es todo. Hay otro elemento de diseño para agregar aún más confusión. Se le han dado varios nombres diferentes (por ejemplo, "Hyperthreading", "SMT", "CMP"), pero todos se refieren a la misma idea básica: una CPU que puede ejecutar múltiples subprocesos simultáneamente, utilizando una combinación de algunos recursos que son independientes para cada subproceso y algunos recursos que se comparten entre los subprocesos. En un caso típico, esto se combina con el paralelismo de nivel de instrucción descrito anteriormente. Para hacer eso, tenemos dos (o más) conjuntos de registros arquitectónicos. Luego tenemos un conjunto de unidades de ejecución que pueden ejecutar instrucciones tan pronto como los recursos necesarios estén disponibles.
Luego, por supuesto, llegamos a sistemas modernos con múltiples núcleos. Aquí las cosas son obvias, ¿verdad? Tenemos N (en algún lugar entre 2 y 256 más o menos, en este momento) núcleos separados, que pueden ejecutar instrucciones al mismo tiempo, por lo que tenemos un caso claro de paralelismo real: la ejecución de instrucciones en un proceso / hilo no lo hace t afecta la ejecución de instrucciones en otro.
Especie de. Incluso aquí tenemos algunos recursos independientes (registros, unidades de ejecución, al menos un nivel de caché) y algunos recursos compartidos (generalmente al menos el nivel más bajo de caché, y definitivamente los controladores de memoria y el ancho de banda a la memoria).
Para resumir: los escenarios simples que a las personas les gusta contrastar entre recursos compartidos y recursos independientes prácticamente nunca suceden en la vida real. Con todos los recursos compartidos, terminamos con algo como MS-DOS, donde solo podemos ejecutar un programa a la vez, y tenemos que dejar de ejecutar uno antes de poder ejecutar el otro. Con recursos completamente independientes, tenemos N computadoras que ejecutan MS-DOS (sin siquiera una red para conectarlas) sin capacidad de compartir nada entre ellas (porque si incluso podemos compartir un archivo, bueno, ese es un recurso compartido, un violación de la premisa básica de que nada se comparte).
Cada caso interesante implica una combinación de recursos independientes y recursos compartidos. Cada computadora razonablemente moderna (y muchas que no lo son en absoluto) tiene al menos alguna capacidad para llevar a cabo al menos algunas operaciones independientes simultáneamente, y casi cualquier cosa más sofisticada que MS-DOS ha aprovechado eso al menos algun grado.
La división agradable y limpia entre "concurrente" y "paralelo" que a la gente le gusta dibujar simplemente no existe, y casi nunca existe. Lo que a la gente le gusta clasificar como "concurrente" generalmente todavía implica al menos uno y, a menudo, más tipos diferentes de ejecución paralela. Lo que les gusta clasificar como "paralelo" a menudo implica compartir recursos y (por ejemplo) un proceso que bloquea la ejecución de otro mientras usa un recurso que se comparte entre los dos.
Las personas que intentan establecer una distinción clara entre "paralelo" y "concurrente" viven en una fantasía de computadoras que nunca existieron.