¿Diferencia entre programación paralela y concurrente?

44

Cuando se observa la programación concurrente, se usan comúnmente dos términos, es decir, concurrente y paralelo.

Y algunos lenguajes de programación específicamente reclaman soporte para programación paralela, como Java .

¿Esto significa que la programación paralela y concurrente son realmente diferentes?

nish1013
fuente
10
Sí, la programación concurrente y paralela son diferentes. por ejemplo, puede tener dos hilos (o procesos) ejecutándose simultáneamente en el mismo núcleo a través del cambio de contexto. Cuando los dos hilos (o procesos) se ejecutan en dos núcleos (o procesadores) diferentes, tiene paralelismo. Entonces, en el primer caso (concurrencia) el paralelismo es solo "virtual", mientras que en el segundo tienes un verdadero paralelismo. Por lo tanto, cada programa paralelo es concurrente, pero lo contrario no es necesariamente cierto.
Massimo Cafaro
1
Ten cuidado aquí. Puede lograr el mismo resultado a través del soporte de idiomas (es decir, extendiendo un lenguaje con nuevas construcciones) o utilizando un enfoque de bajo nivel (por ejemplo, utilizando una biblioteca, como en el caso de MPI y OpenMP). De todos modos, con los procesadores multinúcleo actuales y los sistemas operativos con soporte SMP, el programa que será concurrente si se ejecuta en procesadores de un solo núcleo antiguos, puede ejecutarse en paralelo si el sistema operativo programa los hilos de ejecución del programa en diferentes núcleos. Entonces, la distinción es un poco "borrosa" hoy en día.
Massimo Cafaro
3
Lo que usas para una velocidad de latencia de luz constante. Al mismo tiempo, simulas que la velocidad de la latencia de la luz es un ciclo de reloj. En paralelo, asume que hay un servidor al lado, en distribuido asume que un servidor está en Marte.
1
Robert Harper analiza el tema en dos publicaciones de blog, "Paralelismo no es concurrencia" y "Paralelismo y concurrencia, revisados" , que es posible que desee comprobar.
Basil

Respuestas:

26

Distinga el paralelismo (usando unidades computacionales adicionales para hacer más trabajo por unidad de tiempo) de la concurrencia (administrando el acceso a los recursos compartidos). Enseñe el paralelismo primero porque es más fácil y ayuda a establecer una mentalidad no secuencial.

De "A Sophomoric ∗ Introducción al paralelismo y concurrencia de memoria compartida" por Dan Grossman (versión del 16 de noviembre de 2013)

Rafael
fuente
21

Además de la respuesta de Nish, permítanme recomendar el libro de Simon Marlow sobre programación paralela y simultánea en Haskell o su tutorial más corto . Responden a su primera pregunta desde la perspectiva de Haskell, por lo que podrían ser más adecuados para lectores inclinados teóricamente (Haskell es un lenguaje de programación puramente funcional y vago que está mucho más cerca de las Matemáticas que otros lenguajes).

Citando a partir de ahí:

En muchos campos, las palabras paralelas y concurrentes son sinónimos; no es así en la programación, donde se usan para describir conceptos fundamentalmente diferentes.

Un programa paralelo es uno que usa una multiplicidad de hardware computacional (por ejemplo, múltiples núcleos de procesador) para realizar el cálculo más rápidamente. Se delegan diferentes partes del cálculo a diferentes procesadores que se ejecutan al mismo tiempo (en paralelo), de modo que los resultados se pueden entregar antes que si el cálculo se hubiera realizado secuencialmente.

En contraste, la concurrencia es una técnica de estructuración de programas en la que existen múltiples hilos de control. Nocionalmente, los hilos de control se ejecutan "al mismo tiempo"; es decir, el usuario ve sus efectos intercalados. Si realmente se ejecutan al mismo tiempo o no es un detalle de implementación; Un programa concurrente puede ejecutarse en un único procesador a través de la ejecución intercalada, o en múltiples procesadores físicos.

Recomiendo leer el resto en el tutorial (p. 4), pero permítanme citar algo del resto de esta sección, ya que conecta ambos paradigmas de programación con características cuantitativas y cualitativas de los programas, como la eficiencia, la modularidad y el determinismo.

Mientras que la programación paralela se refiere solo a la eficiencia, la programación concurrente se refiere a la estructuración de un programa que necesita interactuar con múltiples agentes externos independientes (por ejemplo, el usuario, un servidor de base de datos y algunos clientes externos). La concurrencia permite que dichos programas sean modulares; el hilo que interactúa con el usuario es distinto del hilo que habla con la base de datos. En ausencia de concurrencia, dichos programas tienen que escribirse con bucles de eventos y devoluciones de llamadas --- de hecho, los bucles de eventos y devoluciones de llamadas a menudo se usan incluso cuando hay concurrencia disponible, porque en muchos idiomas la concurrencia es demasiado costosa o demasiado difícil de manejar. utilizar.

La noción de "hilos de control" no tiene sentido en un programa puramente funcional, porque no hay efectos que observar y el orden de evaluación es irrelevante. Entonces, la concurrencia es una técnica de estructuración para un código efectivo; en Haskell, eso significa código en la mónada IO.

Una distinción relacionada es entre modelos de programación deterministas y no deterministas. Un modelo de programación determinista es uno en el que cada programa puede dar un solo resultado, mientras que un modelo de programación no determinista admite programas que pueden tener resultados diferentes, dependiendo de algún aspecto de la ejecución. Los modelos de programación concurrentes son necesariamente no deterministas, ya que deben interactuar con agentes externos que causan eventos en momentos impredecibles. Sin embargo, el no determinismo tiene algunos inconvenientes notables: los programas se vuelven significativamente más difíciles de probar y razonar.

Para la programación paralela, nos gustaría utilizar modelos de programación deterministas si es posible. Dado que el objetivo es llegar a la respuesta más rápidamente, preferimos no hacer que nuestro programa sea más difícil de depurar en el proceso. La programación paralela determinista es lo mejor de ambos mundos: las pruebas, la depuración y el razonamiento se pueden realizar en el programa secuencial, pero el programa se ejecuta más rápido cuando se agregan procesadores. De hecho, la mayoría de los procesadores informáticos implementan paralelismo determinista en forma de canalización y unidades de ejecución múltiple.

Si bien es posible hacer una programación paralela usando concurrencia, esa es a menudo una mala elección, porque la concurrencia sacrifica el determinismo. En Haskell, los modelos de programación paralela son deterministas. Sin embargo, es importante tener en cuenta que los modelos de programación deterministas no son suficientes para expresar todo tipo de algoritmos paralelos; existen algoritmos que dependen del no determinismo interno, particularmente problemas que implican buscar un espacio de solución. En Haskell, esta clase de algoritmos solo se puede expresar usando concurrencia.

nickie
fuente
20

La simultaneidad y el paralelismo difieren en los problemas que resuelven y causan, pero no son independientes.

Concurrencia

Ejecutar dos tareas simultáneamente significa que los pasos individuales de ambas tareas se ejecutan de forma intercalada. Si no tiene en cuenta el paralelismo, puede suponer que solo se ejecuta una declaración en cualquier momento, pero no tiene (a priori) ninguna garantía de qué tarea se ejecutará en el siguiente paso.

Esto es útil en algunos aspectos:

  • Programación más clara de tareas independientes en un programa.
  • Permite lidiar con IO mientras se computa (por ejemplo, en GUI).
  • Permite la ejecución de más de un programa a la vez (concurrencia en el nivel del sistema operativo).

Algunos de los principales desafíos son:

  • Mantener la consistencia de los datos.
  • Evite los callejones sin salida y los bloqueos en vivo .
  • Determinar la semántica precisa de los procesos concurrentes.
  • Determine las propiedades estáticas que aseguran la corrección.

Paralelismo

Ejecutar dos tareas en paralelo significa que las declaraciones se ejecutan al mismo tiempo . Esto es principalmente útil para:

  • Mejore el rendimiento del sistema mediante la ejecución de programas en paralelo (por ejemplo, en sistemas de múltiples núcleos).
  • Mejore el tiempo de ejecución de programas individuales utilizando múltiples CPU a la vez.
  • Utilice IO en muchas máquinas (por ejemplo, bases de datos distribuidas).

Los desafíos clave incluyen:

  • Problemas de partición que permiten y desarrollan algoritmos que pueden emplear paralelismo.
  • Minimice las dependencias y la comunicación entre las unidades de cómputo.
  • Todos los problemas traídos por la concurrencia: al menos desde el punto de vista de la memoria, los programas paralelos se parecen a los concurrentes debido a la serialización de los accesos a la memoria.
  • Tratar con soporte de hardware subóptimo.

Vea también esta pregunta para distinguir la computación paralela y distribuida.

Rafael
fuente
5

Una respuesta ligeramente idealizada, tal vez ...

  • La concurrencia es una propiedad de cómo se escribe un programa . Si un programa se escribe utilizando construcciones como bifurcaciones / uniones, bloqueos, transacciones, operaciones de comparación e intercambio atómicas, etc., entonces es concurrente.

  • El paralelismo es una propiedad de cómo se ejecuta un programa . Si un programa se ejecuta en más de una unidad computacional simultáneamente, entonces se está ejecutando en paralelo.

John Wickerson
fuente
1

Hay muchas respuestas sobre esto, pero puede ser confuso. Me gusta pensar de esta manera, ¿y tal vez ayuda ?:

La programación concurrente es un código que no se preocupa por el orden de ejecución. Java es un lenguaje pobre para la programación concurrente, pero hay bibliotecas y marcos para ayudar. JavaScript es un lenguaje excelente para la programación concurrente, y a menudo es difícil cuando quieres escribir algo que no es concurrente (por ejemplo, si quieres forzar el orden de ejecución). La programación concurrente es excelente para la programación dirigida por eventos (donde el orden de ejecución está determinado por los oyentes de eventos, como el código que se ejecuta en su navegador que actúa cuando hace clic en un botón o escribe en un cuadro).

Un ejemplo incluiría la creación de cien solicitudes HTTP. En NodeJS, la solución más simple es abrir las 100 solicitudes a la vez con un método de devolución de llamada, y cuando vuelven las respuestas, se ejecuta un método cada vez. Esa es la programación concurrente. En Ruby, la solución más simple (más común) es abrir una solicitud y manejar la respuesta, abrir la siguiente solicitud y manejar la respuesta, etc. Para muchas solicitudes, NodeJS es más fácil de hacer de manera oportuna, aunque debe ser Tenga cuidado de no dañar el servidor o maximizar sus conexiones salientes (fácil de hacer por error). Puede escribir Ruby de manera concurrente, pero no es así como se escribe la mayoría del código Ruby, y duele un poco hacerlo.

Programación paralelaes un código que puede ejecutarse simultáneamente en múltiples hilos o procesos. Esto le permite optimizar el rendimiento ejecutando el código en múltiples CPU (a menudo, incluyendo múltiples máquinas, como lo haría con algo como Akka). Debido a que NodeJS no es multiproceso y no hay ejecución paralela, no tiene que preocuparse por escribir código seguro para subprocesos (y la mayoría del código JavaScript que he visto no es seguro para subprocesos). En Java, aunque el lenguaje no hace que la programación concurrente sea el patrón normal, la programación paralela está muy integrada y, a menudo, debe preocuparse por la seguridad de los subprocesos. Si está escribiendo un sitio web en Java, normalmente se ejecutará en un contenedor que ejecuta cada solicitud en un hilo separado en la misma memoria,


Algunos de los puntos anteriores dependen del alcance y los límites de los que está hablando. Yo trabajo en sitios web. La mayoría del código Java que veo no es programación concurrente. Claro, si se aleja lo suficiente, el orden que ingresan las solicitudes del cliente no es importante, pero si se acerca más allá de eso, el orden dicta que las cosas se ejecuten. Pero el código está escrito para que las solicitudes puedan ejecutarse en paralelo con muchos objetos compartidos que deben ser seguros para subprocesos.

Mientras tanto, la mayoría del código JavaScript que veo es concurrente: está escrito de manera que el orden de ejecución no es importante en muchos niveles. Pero no está escrito para admitir la ejecución paralela en la memoria compartida. Claro, puede ejecutar el mismo código en paralelo en múltiples procesos, pero los objetos no se comparten, por lo que no es una programación paralela en ningún sentido significativo.

Para lectura adicional, me gustan mucho las ilustraciones en la respuesta superior a esta pregunta aquí: https://www.quora.com/What-are-the-differences-between-parallel-concurrent-and-asynchronous-programming

Jun-Dai Bates-Kobashigawa
fuente