Que representações intermediárias podem ser usadas para raciocinar sobre simultaneidade?

12

Estou tentando entender melhor o que seria necessário para um compilador ser capaz de fazer escolhas inteligentes em relação à simultaneidade em nome do programador. Percebo que há muitos aspectos difíceis desse problema, por exemplo:

  • Garantir que não haja condições de corrida
  • Garantir que o código seja executado simultaneamente não terá efeitos colaterais que afetam o significado semântico do código

  • Decidindo se a sobrecarga de girar threads vale a pena, dado o grau de paralelismo disponível no código

Meu entendimento é que as duas principais representações intermediárias usadas em compiladores modernos são atribuição única estática para linguagens procedurais e orientadas a objetos e continuações passando estilo para linguagens funcionais. Raciocinar sobre qualquer um dos problemas listados acima parece difícil usando essas formas intermediárias. Mesmo as linguagens que, em teoria, deveriam ter a melhor chance de paralelização automática (linguagens funcionais puras como Haskell, com garantia de nenhum efeito colateral), fizeram progressos limitados nessa frente.

Então, minha pergunta é realmente que representações intermediárias foram usadas para tentar resolver esse problema? Existem outras representações que foram usadas na pesquisa acadêmica que eu não conheço e que são mais adequadas para essa tarefa? Esse é um problema que fundamentalmente precisa ser resolvido pelo front end do compilador, manipulando a árvore de sintaxe abstrata antes que a compilação alcance uma representação intermediária?


fonte
Se você escrever seu código de forma funcional, não precisará se preocupar com as condições da corrida ou com os efeitos colaterais.
Robert Harvey
4
Isso não responde muito bem à sua pergunta, mas você pode estar interessado no Cálculo do Processo, que pode ser usado para raciocinar sobre código simultâneo. O exemplo mais conhecido pode ser o cálculo do pi . No entanto, a paralelização automática ainda é um problema amplamente não resolvido e é melhor resolvida projetando linguagens especificamente para fornecer ao compilador determinadas garantias ou usando anotações especiais.
amon
4
O documento que serve de pano de fundo para as coleções simultâneas da Intel (CnC) lista oito padrões simultâneos fundamentais, como produtor-consumidor. Esses padrões simultâneos, por sua vez, dependem de várias propriedades, como imutabilidade e livre de efeitos colaterais. (Eu apreciaria se alguém pode resumir esse papel e pós como uma resposta aqui.)
rwong
Uma das ferramentas teóricas é denominada "Dynamic Single Assignment (DSA)", criada sobre o SSA.
Rwong
@rwong: você pode fornecer uma referência explícita?
Ira Baxter

Respostas:

5

Supõe-se que a simultaneidade de modelagem explicitamente na representação intermediária (IR) seja um requisito necessário. Portanto, uma resposta seria "qualquer IR usado para programas seqüenciais com a adição de algumas operações de simultaneidade", por exemplo, "bifurcação e junção", "paralelo x y". A adição delas torna possível argumentar sobre alguns tipos de simultaneidade, mas não necessariamente facilmente. Também não é óbvio como garantir certas propriedades (liberdade de corrida de dados) sem chegar a uma representação totalmente funcional (o que dificulta a modelagem útil do paralelismo).

Indiscutivelmente Colorido Redes de Petri (CPNs) são uma boa escolha para representar programas com a concorrência. (Os tokens nos CPNs são "coloridos" [têm um tipo] e podem transportar valores; "transições" para estados podem executar aritmética arbitrária nos tokens recebidos para produzir um token possivelmente de cor diferente com valor calculado no "local"). Se você considerar os locais como resultados computados e transições como operadores de modelagem (incluindo um especial para acessar a memória), isso fornecerá o que equivale a um gráfico de fluxo de dados com o qual modelar programas. Você pode facilmente usar isso para fornecer uma interpretação formal às representações clássicas do compilador, como triplos [operador, entrada1, entrada2, saída].

Existem muitas ferramentas para analisar esses gráficos de CPN, incluindo propriedades de computação como ausência de impasse, limite de contagens de token em locais, etc. Os CPNs hierárquicos permitem modelar funções e procedimentos e a noção de "chamadas".

O que essas representações não fazem claramente é facilitar o raciocínio sobre onde alguém poderia paralelizar um aplicativo. Trivialmente, duas subcomputações podem ser paralelas se não tiverem efeito sobre operandos compartilhados (e é por isso que algumas pessoas adoram programas / representações funcionais). Se a sua representação de programa modelar uma memória compartilhada, você poderá modelá-la como monólito e obter o conjunto usual de problemas sobre o raciocínio sobre interações na memória compartilhada, incluindo endereçamento alternativo, etc. Uma maneira de evitar isso é tratar a memória como pedaços isolados com o estado maior do programa sendo alguns deles (semelhantes a árvores); você pode passar esses pedaços em sua representação intermediária. Não há interação entre dois cálculos paralelos se eles não compartilharem partes (por exemplo, subárvores de memória).

Ira Baxter
fonte