Atualmente, estou criando uma linguagem de programação por diversão, onde a ideia é que cada chamada de função / novo bloco (se cláusulas, loops etc.) funcionem em um thread separado. Em vez de criar novos Threads, o padrão deve ser o de fazer isso automaticamente e, se você quiser que ele seja executado no thread principal, precisará especificar isso.
Não sou tão informado sobre programação paralela multithread, mas sei o básico (Futuros, objetos seguros para threads). Portanto, eu estou querendo saber como essa linguagem poderia parecer sintaxe sábia e se é mesmo possível começar? O objetivo não é torná-lo "útil", é mais pela diversão e por uma experiência de aprendizado.
(Desculpe se este é o lugar errado para postar. Se assim for, eu ficaria feliz em me indicar o lugar certo onde uma pergunta como a minha é permitida.)
Respostas:
Leia muito mais sobre continuações e estilo de passagem de continuação (e sua relação com threads ou corotinas). Sugiro ler SICP & Lisp In Small Pieces . Além disso, a Pragmática da Linguagem de Programação fornece uma visão geral útil de várias linguagens e ajudaria você a criar sua própria.
A sintaxe não importa muito para explorar idéias . A semântica importa muito mais. Sugiro que você adote alguma sintaxe do tipo S-expr (para que você possa criar um protótipo usando o Scheme e sua chamada / cc ) primeiro.
Depois que suas idéias ficarem mais claras (após algumas experiências), você poderá discuti-las sobre lambda-the-ultimate , definir uma sintaxe mais sexy (o que realmente importa se você deseja que as pessoas adotem seu idioma, e o que também importa é a qualidade de implementação, uma amostra de software livre, a documentação, a biblioteca padrão, a interface de função externa etc.)
fonte
Você pode estar interessado em ler sobre a pesquisa em dados paralelos Haskell . Se você pesquisar no youtube, Simon Peyton Jones também deu algumas palestras interessantes sobre o assunto.
Se bem me lembro de suas palestras, em pura programação funcional, é quase trivial encontrar oportunidades para criar threads. Seu principal problema em sua pesquisa é ter muitos que têm vida curta, de modo que a sobrecarga de criar threads e comunicar seus resultados supera essencialmente os benefícios do paralelismo. Por exemplo, é fácil ensinar um compilador a girar 100 threads para calcular
sum $ fmap (+1) <100 length vector>
, mas será que a sobrecarga fará valer a pena?O truque é consolidar threads em tamanhos benéficos, sem impor um ônus ao programador para denotar isso manualmente. É um problema difícil que precisa ser resolvido para usar efetivamente futuros PCs com milhares de núcleos.
fonte
É exatamente isso que Erlang faz. Ele lida com a junção dos threads principalmente usando filas. É um conceito brilhante, mas um pouco difícil de entender inicialmente, se o seu plano de fundo for uma linguagem de tipo mais processual. Eu recomendo olhar para ele.
fonte
Primeiro, recomendo que você analise o PROMELA , uma linguagem usada para descrever um algoritmo simultâneo, para que um verificador de modelos possa fazer força bruta para forçar todas as execuções possíveis para verificar se é incapaz de comportamento incorreto. (A programação simultânea é notoriamente difícil de acertar, e é por isso que essas técnicas de verificação são importantes.) Ela não executa todas as construções em threads separados, mas possui sintaxe e semântica bastante estranhas, porque seu foco é o não determinismo de programas concorrentes.
Tornando-se mais abstrato, o π-cálculo é uma bela abordagem para modelar a computação paralela. É difícil entender o assunto, a menos que você compre o livro Sistemas móveis e de comunicação: o cálculo de Pi , de Robin Milner. Isso me ajudou a pensar em computação paralela em um sentido mais amplo que "vários threads acessando uma memória compartilhada". É bastante interessante como declarações condicionais, "gotos" e assim por diante podem ser construídas a partir de primitivas mais simples e naturalmente paralelas.
Com relação à sintaxe ... a melhor maneira de resolver isso é escrever alguns programas de amostra. Escreva um programa para classificar uma matriz ou, simultaneamente, faça ping em vários servidores e relate qual deles responde mais rapidamente, ou tente resolver um labirinto em paralelo, ou algo assim. Enquanto você faz isso, as coisas que estão faltando na sintaxe se tornam aparentes e você pode adicioná-las. Depois de adicionar várias coisas, pergunte-se se elas têm algo em comum e, se houver, talvez você encontre uma abordagem mais simples que possa servir a vários propósitos.
fonte
Projetos semelhantes foram tentados no passado. Sugiro ler os clássicos para saquear idéias. (Todos os links vão para Wikipedia)
Unidade Esta linguagem foi / é usada para ensinar programação paralela. Eu não acho que foi realmente implementado. A sintaxe é um pouco enigmática, mas basicamente você tem uma coleção de instruções executadas em ordem desconhecida e repetidamente até que não haja mais nada a ser feito. É o mais próximo do que você pede.
Occam Este idioma foi projetado para ser usado de verdade, mas nunca foi realmente utilizado. Aqui existe uma palavra-chave PAR, o que significa que uma lista de instruções deve ser executada em paralelo.
Erlang Outra língua do mundo real. Este é usado pela empresa de telecomunicações Ericsson e tem muitos seguidores. Eles trabalharam muito para tornar o paralelismo prático e utilizável.
Google GO Este é o meu favorito do grupo. Conceitualmente, da mesma forma que Erlang, mas com melhor sintaxe e o peso do Google por trás disso. O que possivelmente poderia dar errado?
Gostaria de encerrar com um aviso: é muito difícil acertar o paralelismo. A maioria dos erros nos programas modernos é o resultado de erros . Tem certeza de que deseja ir para lá?
fonte
É possível, mas não seria útil para mais de 99% de todos os aplicativos possíveis. A lógica é tipicamente associada à sequência, é um fluxo. Passo a passo, você chega a uma solução para um problema e a ordem das etapas é importante, principalmente porque a saída de uma etapa será inserida na próxima.
Nos poucos casos, você tem muitas tarefas que podem ser executadas independentemente uma da outra; geralmente é barato configurá-las sequencialmente antes de deixá-las executar em paralelo.
Então, acho que seria melhor gastar seu tempo aprendendo a usar os recursos de vários threads na sua linguagem de programação favorita.
fonte
Clojure pode valer a pena dar uma olhada em algumas idéias.
http://clojure-doc.org/articles/language/concurrency_and_parallelism.html
Aqui estão algumas considerações: Se chamarmos uma unidade de computação que pode ser executada independentemente, uma tarefa: 1. As tarefas são independentes, portanto, podem ser executadas simultaneamente 2. Tarefas diferentes requerem recursos diferentes e demoram tempos para serem executadas 3. Portanto, as tarefas devem ser agendadas para rendimento máximo 4. O único programa em posição de atuar como agendador é o sistema operacional
Coisas como o envio central de maçãs são uma tentativa de fornecer um agendador desse tipo.
O acima exposto significa que a responsabilidade de executar tarefas não é necessariamente a da linguagem de programação.
Um segundo pensamento é reduzir o peso da programação de sistemas paralelos, tanto quanto possível. A única maneira de fazer isso é remover qualquer especificação de como algo deve ser feito do programa. Um programa deve especificar apenas o que deve ser feito e o resto deve acontecer automaticamente.
O exposto acima provavelmente significa que linguagens dinâmicas e compilação just in time são o caminho a percorrer.
fonte
O que você procura é chamado paralelismo implícito, e há linguagens que exploraram esse conceito, como o Sun / Oracle's Fortress . Entre outras coisas, (potencialmente) executa loops em paralelo.
Infelizmente, foi descontinuado e há muitos links inativos por aí, mas você ainda pode encontrar alguns PDFs flutuando por aí, se pesquisar no Google o suficiente:
https://www.eecis.udel.edu/~cavazos/cisc879-spring2008/papers/fortress.pdf (a especificação do idioma)
http://stephane.ducasse.free.fr/Teaching/CoursAnnecy/0506-Master/ForPresentations/Fortress-PLDITutorialSlides9Jun2006.pdf
http://www.oracle.com/technetwork/systems/ts-5206-159453.pdf
http://dl.acm.org/citation.cfm?id=1122972 (paywalled)
É importante notar que você normalmente não deseja iniciar um encadeamento real para cada declaração / expressão, pois criar e iniciar encadeamentos costuma ser caro - em vez disso, você teria um conjunto de encadeamentos nos quais publica alguns trabalhos que precisam ser executados . Mas isso é um detalhe de implementação.
fonte
Embora não seja uma linguagem de programação, você deve dar uma olhada no VHDL . É usado para descrever circuitos digitais, que naturalmente fazem tudo em paralelo, a menos que você diga especificamente para fazê-lo em série. Isso pode lhe dar algumas idéias sobre como projetar sua linguagem e para que tipo de lógica ela pode ser adequada.
fonte
Isso pode ser simulado facilmente em C ++. Apenas certifique-se de que "toda" chamada de função * seja implementada por a
std::future
. A manipulação do valor de retorno é feita simplesmente chamando.get()
o futuro.Portanto, você pode criar um protótipo de sua linguagem compilando em C ++. Isso também nos diz como seria a sintaxe: a principal diferença é que você separa o ponto de chamada (onde os argumentos de entrada são fornecidos) do ponto de retorno (onde a saída da função é usada).
(*) Eu digo "todas as funções", mas cabe a você o que conta como uma função. É
memset
uma função intrínseca ou? A atribuição de números inteiros ou de tipos definidos pelo usuário é uma chamada de função?fonte