Executive summary : Axum est un Domain-Specific Language basé sur C#, et qui permet de mettre en place des agents de traitement parallélisés, chacun ne pouvant modifier que les données qui appartiennent à son domaine, et communiquant obligatoirement avec les autres agents / domaines par des canaux définis à l’avance. Ce principe permet de pousser à son comble l’immuabilité, et donc d’éviter au mieux les problèmes de locks.
Session présentée par Yann Schwartz et Bruno Boucard. On commence par reprendre ce qui a été dit à la session d’hier sur la programmation parallèle, à savoir principalement qu’on ne peut plus augmenter la fréquence, et qu’il faut donc passer à la multiplication des cœurs. Ceci provoque bien sûr un changement de paradigme dans le développement.
J’en profite pour citer l’auteur de la phrase “The free lunch is over”, dont une précédente session a parlé : Il s’agit d’Herb Sutter.
La problématique de complexité en programmation parallèle
La grosse difficulté sur de la programmation parallèle est la même chose qui pousse à l’AOP : on mélange le code métier, fonctionnel, avec du code de gestion d’erreur, des évènements pour le fork / join, des constantes pour retrouver le nombre de processeurs, etc. Bref, ce n’est pas facile à maintenir. De plus, on joue sur des threads qui sont moins coûteux bien sûr qu’un processus, mais sur lesquels il faut faire très attention tout de même en termes de ressources. Du coup, il y a également un problème en termes de fiabilité de code, et ce problème augmente de manière exponentielle avec la complexité du traitement.
Même en .NET, langage un peu plus évolué que C++, tout de même, si on met en place les appels asynchrones avec des delegate, ça marche facilement sur des exemples simples, mais sur des cas plus complexes, il devient difficile de savoir par où on passe en termes de fonction, à part en mettant un debugger en place. Bref, en mode asynchrone, on perd l’intention fonctionnelle du code.
Nouvelles approches
Pour réduire ces problèmes, il faut prendre de la hauteur par rapport au système, de la même manière que lorsqu’on a délégué la gestion mémoire au langage avec les GC. On a donc des librairies de parallélisation qui permettent d’abstraire un peu la gestion des threads. Par exemple, Task Parallel Library de Microsoft.
La difficulté d’atomicité possède déjà une réponse connue de tous, à savoir la gestion transactionnelle. Pour éviter les locks, on part sur des fonctions avec des données immuables, des fonctions sans effets de bord, etc. C’est faisable avec des langages de type Haskell ou Clojure, mais avec des langages de type .NET où on peut faire des manipulations très larges sur les fichiers, etc., on ne peut pas toujours mettre en place le rollback. Si on pousse le concept au bout, on arrive sur des langages de type Erlang où tout est immuable, et on ne travaille que par recopie de données.
Pour des données, on utilise TPL, PLinq. Pour des tâches, TPL est adapté. Pour des flux de données ou toute forme de workflow, Axum est une bonne solution.
Le modèle acteur permet de ne pas se soucier de si le traitement est synchrone ou pas : chaque acteur a ses propres données, et ne communique que par messages avec des copies de données. Typique de Erlang, F#, et Axum. L’idée d’Axum est d’appliquer ce concept d’agent sur un langage objet, plus familier que du fonctionnel pur.
L’approche Axum
Axum est un DSL, basé sur .NET. On a donc bien sûr accès à toute la BCL. Axum est encore en incubation et on ne sait pas si il sortira, et le cas échéant, sous quelle forme (intégrée à C# ou F#, etc.)
Les concepts d’Axum sont les suivants :
- Domaine : périmètre des données partagées par les agents. Les données peuvent être partagées, mais il faut le faire de manière explicite.
- Agents : unité de traitement isolé.
- Channel : modalité d’échange des messages entre agents.
- Schéma : pour définir le contenu des messages.
La difficulté pour le parallélisme est de décomposer les traitements. L’approche d’Axum est de facilité la modularisation et la composition. L’application est découpée en domaines, accessibles par les agents du domaine. Les communications sur les channels sont asynchrones.
Demo
Impossible de noter le code, donc je laisse de côté, et je vous renvoie aux webcasts si vous êtes intéressés.
Note : la RC de Visual Studio 2010 est sortie cette nuit.
La démo n’arrive pas au bout à cause d’un problème d’effet de bord sur le Console.WriteLine. L’intervenant explique qu’il a fallu annoter la CLR dans Axum pour savoir quelle fonction a un effet de bord. Il faudrait à priori mettre les opérations dans un ordre différent, mais la correction n’est pas montrée. C’est un peu gênant pour la crédibilité de la solution, même s’il ne faut pas oublier qu’on est sur de la technologie en incubation.
Explications supplémentaires
Un agent peut être déclaré en lecture, en écriture ou en ne faisant aucun accès à la donnée. Du coup, c’est Axum qui se charge des éventuels ReaderWriterLock.
Le schéma ressemble un peu à un DataContract dans WCF, sauf qu’on est obligatoirement sérialisable, et largement immuable.
Le protocole d’échange de messages définit l’ordre attendu des messages, comme les transitions d’un workflow, et supporte des contraintes. Ceci permet de limiter les risques d’erreurs fonctionnels sur les envois de message par les agents. Par exemple, ils ne vont pas pouvoir envoyer un message de progression après avoir lancé un message de complétion.
Des opérateurs spécifiques permettent de mettre en place des communications plus évoluées, comme du broadcast (communication type Publish & Subscribe, où on envoie le message à tout le monde), du forward (on demande à passer à la suite), du forward-once (appel de la suite, mais en mode Fire & Forget), etc.
Limitation importante du réseau de calcul : pour l’instant, il ne peut fonctionner que sur un AppDomain.
Conclusion
Très puissant, et très bonne approche pour résoudre des problèmes complexes, mais personnellement, ça me convainc de plus en plus qu’une parallélisation est très intéressante pour un développeur si elle peut être décomposée de manière fonctionnelle. Quand il faut aller dans des solutions aussi complexes, il y a d’abord une étape de modélisation qui est complexe.