¿Qué representaciones intermedias se pueden usar para razonar sobre la concurrencia?


12

Estoy tratando de comprender mejor lo que se necesitaría para que un compilador pueda tomar decisiones inteligentes con respecto a la concurrencia en nombre del programador. Me doy cuenta de que hay muchos aspectos difíciles de este problema, por ejemplo:

  • Asegurarse de que no hay condiciones de carrera
  • Asegurarse de que el código se ejecute simultáneamente no tendrá efectos secundarios que afecten el significado semántico del código

  • Decidir si vale la pena la sobrecarga de los subprocesos, dado el grado de paralelismo disponible en el código

Según tengo entendido, las dos representaciones intermedias principales que se utilizan en los compiladores modernos son asignaciones únicas estáticas para lenguajes de procedimiento y orientados a objetos y estilo de paso de continuación para lenguajes funcionales. Razonar sobre cualquiera de los problemas enumerados anteriormente parece difícil usando estas formas intermedias. Incluso los lenguajes que, en teoría, deberían tener la mejor oportunidad de paralelización automática (lenguajes funcionales puros como Haskell con la garantía de que no hay efectos secundarios) han tenido un progreso limitado en este frente.

Entonces, mi pregunta es, ¿qué representaciones intermedias se han utilizado para tratar de abordar este problema? ¿Hay otras representaciones que se hayan utilizado en la investigación académica que no conozca que se adapten mejor a esta tarea? ¿Es este un problema que el compilador tiene que resolver fundamentalmente manipulando el árbol de sintaxis abstracta antes de que la compilación alcance una representación intermedia?


Si escribe su código de manera funcional, no tendrá que preocuparse por las condiciones de carrera o los efectos secundarios.
Robert Harvey

44
Esto no responde a su pregunta, pero podría estar interesado en Process Calculi, que puede usarse para razonar sobre el código concurrente. El ejemplo más conocido podría ser el cálculo Pi . Sin embargo, la paralelización automática sigue siendo un problema en gran medida sin resolver y se aborda mejor diseñando lenguajes específicamente para proporcionar ciertas garantías al compilador o mediante anotaciones especiales.
amon

44
El documento que sirve de telón de fondo para Intel Concurrent Collections (CnC) enumera ocho patrones concurrentes fundamentales, como Producer-Consumer. Estos patrones concurrentes a su vez dependen de una serie de propiedades, como la inmutabilidad y la ausencia de efectos secundarios. (Agradecería si alguien puede resumir ese documento y publicarlo como respuesta aquí.)
rwong

Una de las herramientas teóricas se llama "Asignación dinámica única (DSA)", construida sobre SSA.
rwong

@rwong: ¿puede proporcionar una referencia explícita?
Ira Baxter

Respuestas:


5

Uno supondría que la concurrencia de modelado explícitamente en la representación intermedia (IR) era un requisito necesario. Entonces, una respuesta sería: "cualquier IR utilizada para programas secuenciales con la adición de algunas operaciones de concurrencia", por ejemplo, "bifurcación y unión", "paralelo x y". Agregar estos hace posible razonar sobre algunos tipos de concurrencia, pero no necesariamente fácilmente. Tampoco es obvio cómo garantizar ciertas propiedades (libertad de carrera de datos) sin llegar a una representación completamente funcional (lo que dificulta modelar el paralelismo de manera útil).

Posiblemente, las redes de Petri coloreadas (CPN) son una buena opción para representar programas con concurrencia. (Los tokens en los CPN son "coloreados" [tienen un tipo] y pueden transportar valores; las "transiciones" en estados pueden realizar operaciones aritméticas arbitrarias en tokens entrantes para producir un token posiblemente de diferente color con un valor calculado en el "lugar"). Si piensa que los lugares son resultados calculados y transiciones como operadores de modelado (incluido uno especial para acceder a la memoria), esto le proporciona lo que equivale a un gráfico de flujo de datos con el que modelar programas. Puede usar esto fácilmente para dar una interpretación formal a las representaciones clásicas del compilador, como triples [operador, entrada1, entrada2, salida].

Existen muchas herramientas para analizar tales gráficos de CPN, incluidas las propiedades informáticas como la ausencia de puntos muertos, la limitación de los recuentos de tokens en lugares, etc. Los CPN jerárquicos le permiten modelar funciones y procedimientos y la noción de "llamadas".

Lo que estas representaciones no hacen claramente es facilitar razonar sobre dónde se podría paralelizar una aplicación. Trivialmente, dos subcomputaciones pueden ser paralelas si no afectan a los operandos compartidos (razón por la cual algunas personas aman los programas / representaciones funcionales). Si la representación de su programa modela una memoria compartida, puede modelarla como un monolito y obtener el conjunto habitual de problemas sobre el razonamiento sobre las interacciones en la memoria compartida, incluido el direccionamiento con alias, etc. Una forma de evitar esto es tratar la memoria como fragmentos aislados con el estado más amplio del programa es un ensamblaje (similar a un árbol) de estos; se puede pasar estos trozos en su representación intermedia. No hay interacción entre dos cálculos paralelos si no comparten fragmentos (por ejemplo, subárboles de memoria).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.