Je suis actuellement en train de créer un langage de programmation pour le plaisir où l'idée est que chaque appel de fonction/nouveau bloc (clauses if, boucles, etc.) fonctionnera dans un thread séparé. Au lieu de créer de nouveaux threads, la norme devrait être de le faire automatiquement, et si vous souhaitez qu'il s'exécute dans le thread principal, vous devrez le spécifier.
Je ne suis pas très informé sur la programmation parallèle multi-thread mais je connais les bases (Futures, objets thread-safe). Je me demande donc à quoi pourrait ressembler un tel langage sur le plan de la syntaxe et s'il est même possible de commencer par? Le but n'est pas de le rendre "utile", c'est plus pour le plaisir et une expérience d'apprentissage.
(Je suis désolé si ce n'est pas le bon endroit pour poster. Si c'est le cas, j'apprécierais volontiers que vous me dirigiez vers le bon endroit où une question comme la mienne est autorisée.)
chaque appel de fonction/nouveau bloc (clauses if, boucles, etc.) fonctionnera dans un thread séparé.
En savoir plus sur continuations et style de passage de continuation (et leur relation avec les threads ou coroutines) je suggère de lire SICP & LISP In Small Pièces . En outre, Programming Language Pragmatics donne un aperçu utile de plusieurs langages et vous aiderait à concevoir le vôtre.
Je me demande donc à quoi pourrait ressembler un tel langage sur le plan de la syntaxe
La syntaxe n'a pas beaucoup d'importance pour explorer idées. Sémantique compte beaucoup plus. Je suggère d'adopter une syntaxe similaire à S-expr (afin que vous puissiez prototyper en utilisant Scheme et son call/cc ) dans un premier temps.
Une fois que vos idées sont plus claires (après quelques expérimentations), vous pouvez en discuter sur lambda-the-ultimate , vous pouvez définir une syntaxe plus sexy (ce qui compte vraiment si vous voulez que les gens adoptent votre langue, et ce qui compte aussi, c'est la qualité de l'implémentation, un exemple d'implémentation de logiciel gratuit, la documentation, la bibliothèque standard, l'interface de fonction étrangère, etc.)
Vous pourriez être intéressé par la lecture de la recherche sur données parallèles Haskell . Si vous effectuez une recherche sur youtube, Simon Peyton Jones a également donné des conférences intéressantes sur le sujet.
Si je me souviens bien de ses exposés, en programmation fonctionnelle pure, il est presque trivial de trouver des opportunités de créer des threads. Son principal problème dans sa recherche est d'avoir trop qui sont de trop courte durée, de sorte que la surcharge de création de threads et de communication de leurs résultats l'emporte essentiellement sur les avantages de parallélisme. Par exemple, il est facile d'apprendre à un compilateur à faire tourner 100 threads pour calculer sum $ fmap (+1) <100 length vector>
, mais la surcharge en vaudra-t-elle la peine?
L'astuce consiste alors à consolider les threads dans des tailles avantageuses, sans imposer au programmeur la charge de l'indiquer manuellement. C'est un problème difficile qui doit être résolu afin d'utiliser efficacement les futurs PC avec des milliers de cœurs.
C'est exactement ce que fait Erlang. Il gère la jonction des threads principalement à l'aide de files d'attente. C'est un concept génial mais un peu difficile à comprendre au départ si votre arrière-plan est plus des langages de type procédural. Je recommande fortement de l'examiner.
Tout d'abord, je vous recommande de regarder PROMELA , un langage utilisé pour décrire un algorithme simultané afin qu'un vérificateur de modèle peut forcer toutes les exécutions possibles pour vérifier qu'il est incapable d'un comportement incorrect. (La programmation simultanée est notoirement difficile pour bien faire, c'est pourquoi de telles techniques de vérification sont importantes.) Il n'exécute pas toutes les constructions dans des threads séparés, mais il a une syntaxe et une sémantique plutôt étranges parce que son l'accent est mis sur le non-déterminisme des programmes simultanés.
Plus abstrait, le π-calcul est une belle approche pour modéliser le calcul parallèle. Il est difficile de se mettre en tête sans avoir le livre Systèmes communicants et mobiles: le calcul Pi, par Robin Milner. Cela m'a aidé à penser au calcul parallèle dans un sens plus large que "plusieurs threads accédant à une mémoire partagée". Il est assez intéressant de voir comment les instructions conditionnelles, les "gotos" et ainsi de suite peuvent être construits à partir de primitives simples et naturellement parallèles.
En ce qui concerne la syntaxe ... la meilleure façon de résoudre ce problème est d'écrire quelques exemples de programmes. Écrivez un programme pour trier un tableau, ou envoyez simultanément une requête ping à plusieurs serveurs et signalez lequel répond le plus rapidement, ou essayez de résoudre un labyrinthe en parallèle, ou autre chose. Pendant que vous effectuez cette opération, les éléments manquants dans votre syntaxe deviendront apparents et vous pourrez les ajouter. Après avoir ajouté plusieurs éléments, demandez-vous s'ils ont quelque chose en commun et, dans l'affirmative, vous pourrez peut-être trouver une approche plus simple pouvant servir à plusieurs fins.
Des projets similaires ont été tentés par le passé. Je suggère de lire sur les classiques pour piller les idées. (Tous les liens vont à Wikipedia)
nity Cette langue a été/est utilisée pour enseigner la programmation parallèle. Je ne pense pas qu'il ait été effectivement mis en œuvre. La syntaxe est quelque peu cryptique, mais fondamentalement, vous avez une collection d'instructions qui s'exécutent dans un ordre inconnu et à plusieurs reprises jusqu'à ce qu'il n'y ait plus rien à faire. C'est le plus proche de ce que vous demandez.
Occam Ce langage a été conçu pour être réellement utilisé mais il n'a jamais vraiment fait son chemin. Ici, il y a un mot-clé PAR qui signifie qu'une liste d'instructions doit être exécutée en parallèle.
Erlang Une autre langue du monde réel. Celui-ci est utilisé par la société de télécommunications Ericsson et a tout un suite. Ils ont beaucoup travaillé pour rendre le parallélisme pratique et utilisable.
Google GO C'est mon préféré du groupe. Conceptuellement la même chose qu'Erlang, mais avec une meilleure syntaxe et le poids de Google derrière. Qu'est-ce qui pourrait mal tourner?
Je voudrais terminer par un avertissement: le parallélisme est très difficile à obtenir correctement. La plupart des bogues dans les programmes modernes sont le résultat d'une erreur . Êtes-vous sûr de vouloir y aller?
C'est possible mais cela ne serait pas utile pour 99 +% de toutes les applications imaginables. La logique est typiquement liée à une séquence, c'est un flux. Étape par étape, vous parvenez à une solution à un problème et l'ordre des étapes importe, principalement parce que la sortie d'une étape sera entrée pour la suivante.
Dans les rares cas où vous avez beaucoup de tâches qui peuvent être effectuées indépendamment les unes des autres, il est généralement bon marché de les configurer séquentiellement avant de les laisser s'exécuter en parallèle.
Donc, je pense que votre temps serait mieux consacré à apprendre à utiliser les fonctionnalités multi-threading dans votre langage de programmation préféré.
Bien qu'il ne s'agisse pas d'un langage de programmation en tant que tel, vous devriez jeter un œil à VHDL . Il est utilisé pour décrire les circuits numériques, qui font naturellement tout en parallèle, sauf si vous lui demandez spécifiquement de le faire en série. Cela pourrait vous donner des idées à la fois sur la façon de concevoir votre langage et sur le type de logique auquel il pourrait convenir.
Clojure peut valoir le coup d'œil pour quelques idées.
http://clojure-doc.org/articles/language/concurrency_and_parallelism.html
Voici quelques réflexions: Si nous appelons une unité de calcul qui peut être effectuée indépendamment une tâche: 1. Les tâches sont indépendantes et peuvent donc être exécutées simultanément 2. Différentes tâches nécessitent des ressources différentes et prennent différents temps pour s'exécuter 3. Par conséquent, les tâches doivent être planifiées pour un débit maximal 4. Le seul programme en mesure d'agir en tant que planificateur est le système d'exploitation
Des choses comme la répartition centrale des pommes sont une tentative de fournir un tel programmateur.
Ce qui précède signifie que la responsabilité de l'exécution des tâches n'est pas nécessairement celle du langage de programmation.
Une seconde réflexion est de réduire au maximum la charge de programmation des systèmes parallèles. La seule façon de le faire est de supprimer toute spécification de la façon dont quelque chose doit être fait du programme. Un programme ne doit spécifier que ce qui doit être fait et le reste doit se produire automatiquement.
Ce qui précède signifie probablement que les langages dynamiques et la compilation juste à temps sont la voie à suivre.
Ce que vous recherchez s'appelle le parallélisme implicite, et il existe des langages qui ont exploré ce concept, comme celui de Sun/Oracle Fortress . Entre autres choses, il exécute (potentiellement) des boucles en parallèle.
Malheureusement, il a été interromp et il y a beaucoup de liens morts là-bas, mais vous pouvez toujours trouver quelques PDF flottant là-bas, si vous cherchez assez sur Google:
https://www.eecis.udel.edu/~cavazos/cisc879-spring2008/papers/fortress.pdf (la spécification de langue)
http://www.Oracle.com/technetwork/systems/ts-5206-159453.pdf
http://dl.acm.org/citation.cfm?id=1122972 (paywalled)
Il convient de noter que vous ne voudriez généralement pas démarrer un thread réel pour chaque instruction/expression, car la création et le démarrage de threads ont tendance à être coûteux - à la place, vous auriez pool de threads sur lesquels vous publiez des bits de travail à faire . Mais c'est un détail d'implémentation.
Cela peut être simulé assez facilement en C++. Assurez-vous simplement que "chaque" * appel de fonction est implémenté par un std::future
. La gestion de la valeur de retour se fait simplement en appelant .get()
sur le futur.
Par conséquent, vous pouvez prototyper votre langage en le compilant en C++. Cela nous indique également à quoi ressemblerait la syntaxe: la principale différence est que vous séparez le point d'appel (où les arguments d'entrée sont fournis) du point de retour (où la sortie de la fonction est utilisée).
(*) Je dis "toutes les fonctions" mais c'est à vous de décider ce qui compte comme fonction. memset
est-il intrinsèque ou une fonction? L'affectation d'entiers ou l'affectation de types définis par l'utilisateur est-elle un appel de fonction?