Blog

  • Mikaël Dautrey
Méthodes de programmation dépassées, système ralenti, merci de mettre à jour

Romain Jouin m'a aimablement invité à faire une présentation devant les étudiants de Leonard de Vinci à propos de mes opinions, en tant que professionnel de l'IT, sur l'évolution des architectures informatiques.

Plutôt que de paraphraser les experts, et en prenant inspiration sur le magazine périodique ACM queue, qui présente périodiquement des papiers scientifiques ayant un intérêt pour les ingénieurs dans l'industrie, j'ai décidé d'organiser ma présentation autour de quelques articles scientifiques plus ou moins récents qui m'ont particulièrement inspiré. Le support de présentation, qui est principalement une citation d'une liste d'articles, est disponible en téléchargement ici. J'espère que les citations sont suffisamment explicites dans le PowerPoint. Si un auteur souhaite que sa citation soit retirée, il peut me contacter directement et je modifierai le support en conséquence.

A travers cette présentation, mon principal objectif était de démontrer que, aujourd'hui, chaque programmeur doit comprendre les problématiques de parallélisme et développer, autant que possible, du code nativement parallélisable. La programmation séquentielle est finie. Si cette idée peut être assez familière - ou pas - à des développeurs juniors, elle est plus révolutionnaire pour les plus anciens comme moi, qui ont appris à programmer de manière séquentielle.

Mon discours tourne autour de trois sujets :

  • en premier lieu, il présente des faits qui montrent les limites du parallélisme "caché", c'est-à-dire, le parallélisme dont le programmeur ne se rend pas compte et qui le conduit à croire que le calcul parallèle et la concurrence sont gérés par le système ;
  • en deuxième, il présente certains résultats de travaux réalisés ou en cours pour améliorer la situation ;
  • enfin, il évoque quelques questions ouvertes autour du parallélisme et des systèmes distribués.

Calcul parallèle : des configurations et des besoins divers

Une CPU et plusieurs programmes s'exécutant en parallèle : la concurrence

Les années 90 (et même avant) ont vu l'essor des systèmes multi-tâche. Les systèmes Unix (et Linux) implémentent un multi-tâche préemptif quand Windows 3.x offrait un multi-tâche, qui a ensuite évolué vers un scheduler système plus proche de celui d'Unix dans Windows NT (cet article Wikipedia donne plus de détails à ce sujet).

Plus récents, les OS pour smartphone offrent également un multi-tâche adapté, à la fois à l'usage puisque la fonction téléphonique doit pouvoir préempter pour permettre à l'utilisateur de répondre par exemple, et au contexte embarqué avec des ressources limitées et des objectifs de durée de vie de la batterie. Vous trouverez des informations intéressantes à ce sujet dans cet article à propos du multi-tâche dans Android par exemple.

Le multi-tâche répond au besoin de partager des ressources entre des processus concurrents et indépendants.

Un programme et plusieurs CPU : le parallélisme

Le parallélisme est une solution pour exécuter un programme dont les besoins vont au delà des capacités d'un seul système. Plusieurs stratégies sont possibles, augmenter les performances du système (scale up), vitesse du processeur, jeu d'instructions adapté, ou déléguer certaines tâches à des périphériques spécialisés du système (heterogeneous computing), ou répartir les tâches sur plusieurs systèmes équivalents (scale out). Ces stratégies ont été développés ou sont encore développées :

  • Jeux d'instruction RISC versus Cisc (informations intéressantes ici)
  • Cartes PCIe (cartes graphiques, GPU, cartes FPGA, cartes DSP)
  • Processeurs multi-coeur
  • Architectures distribuées HPC (High Performance Computing) ~ super-computer
  • Architectures distribuées Big Data

Un programme et plusieurs noeuds de données à traiter : (big data ?)

Lorsque la donnée est trop volumineuse pour être stockée sur un seul noeud ou nécessite beaucoup de mémoires vives pour s'exécuter, il est avantageux de déplacer le programme vers la donnée plutôt que de transférer la donnée vers le programme. Les architectures big data cherchent à répondre à ce cas d'usage.

Les limites du parallélisme caché

En février 2019, à la suite de leur "Turing Lecture", John L. Hennessy and David A. Patterson ont publié un article intitulé A New Golden Age for Computer Architecture où ils récapitulent les défis majeurs de l'informatique :

  • La fin de l'accroissement de puissance des CPU avec la fin de la loi de Moore
  • les limites de consommation des CPUs (Dennard scaling) et de refroidissement de celles-ci, qui réduisent leur temps d'utilisation à pleine capacité en fonction de l'architecture de refroidissement du système
  • les limites du parallélisme au niveau des CPU et de l'exécution spéculative

Ils suggèrent également les solutions sur lesquelles le secteur travaille pour répondre à ces problématiques :

  • Des architectures matérielles spécialisées en fonction des calculs à réaliser
  • Des langages de programmation qui facilient le développement de programmes parallèlisables
  • De nouvelles méthodes de développement des matériels, plus agiles

En résumé, les méthodes utilisées par le passé, optimisation des CPUs, augmentation de leur capacité, augmentation de fréquence, ne fonctionnent plus. Des sources d'optimisation des performances et d'accélération des systèmes existent à condition que les programmeurs et designers revoient leur manière de travailler pour penser parallèle et distribué et d'utiliser de nouvelles ressources, langages de programmation adaptés, plateformes de calcul spécifiques par domaine.

Travaux réalisés ou en cours

Domain specific architectures, polyvalence versus performance

En dehors du secteur du HPC, les fabricants de cartes graphiques (AMD, Intel, NVidia) ont été parmi les premiers à proposer des cartes spécifiques pour réaliser des calculs avec un modèle d'exécution standardisé autour d'OpenCL et des librairies propriétaires comme (CUDA avec deux niveaux de standardisation possible :

  • un standard d'échange entre la carte spécialisée et le système
  • un standard d'exécution / de parallélisme des instructions

Des cartes spécifiques ont également été développées pour le machine learning comme la TPU (Tensorflow Processing Unit) de Google. Cet article donne un bon aperçu des performances affichées par les TPU. Des startups travaillent actuellement à la conception de serveurs dédiés au traitement des réseaux de neurones.

IBM Watson, comme les projets en cours pour (essayer) de construire des machines quantiques, sont des initiatives voisines, avec l'objectif de construire des architecures propriétaires, fermées, accessibles en service cloud uniquement, avec une interface d'accès open-source.

Enfin, des librairies et des outils sont fournis, comme Xilink Sdaccell, pour accélérer le développement de cartes d'accélération serveur FPGA.

Du côté des architectures plus traditionnelles X86/IA64 architecture et des chipsets, des progrès très importants ont été réalisés, dont l'impact sur la conception des logiciels ne sont pas encore tous mesurables :

  • Performances et capacité de la RAM
  • Bus d'accès aux disques, disques SSD
  • Remplacement des disques par de la NVMe
  • Réseau 10Gbe
  • ...

A court terme, la portabilité et la standardisation des solutions ne sont pas assurées. Choisir un chemin d'évolution face à cette débauche de solutions n'est pas chose facile... stratégie attentiste, pari volontariste ou opportunisme ciblé ?

Etonnamment, ou pas si l'on considère que le secteur du réseau est plus mûr que le machine learning, les équipementiers réseau migrent vers solutions software-defined et des architectures matérielles standard à base de linux. A moyen terme, nous pouvons prévoir que le domaine du machine learning se consolidera autour de quelques solutions :

  • quelques frameworks logiciels comme Keras, Tensorflow, Pytorch
  • des drivers permettant de brancher des matériels hétérogènes sur ces frameworks
  • quelques interfaces standards pour accéder aux familles de cartes les plus courantes (GPU, FPGA, xPU)

Fondements théoriques

Depuis les premières réflexions, comme l'article de Leslie Lamport sur la mesure du temps dans les systèmes distribués, les travaux théoriques sur les systèmes distribués et la programmation parallèle se sont développés. Des algorithmes ont été inventés pour résoudre les difficultés émaillant le développement d'un système distribué. L'ouvrage de référence de Nancy A. Lynch, "Distributed Algorithms", en présentent un certain nombre. Le fonctionnement distribué est une évolution majeure par rapport à la conception traditionnelle de l'exécution d'un programme type ruban de Turing ou machine de Von Neumann.

Langages de programmation et frameworks

Les architectures Big data et les bases de données distribuées, par exemple, implémentent des algorithmes distribués classiques ou s'appuient sur des travaux théoriques dans le domaine sans que leurs utilisateurs/développeurs ne le sachent ni n'en perçoivent les implications. Pour ne cite que quelques uns, Apache Spark s'appuie sur les DAGs, Directed Acyclic Graphs, pour organiser et ordonnancer ses traitements. Raft, une librairie de traitement distribué en Go, implémente un algorithme de consensus. MongoDB utilise également un algorithme de consensus, qui a évolué au fil des versions. Et le modèle d'exécution de Tensorflow repose sur des résultats théoriques de la théorie des graphes.

Les langages de programmation offrent un panorama nuancé. Quelques exemples sont intéressants à mentionner :

Certains auteurs pense que le C n'est pas un bon langage de programmation système parce qu'il s'appuie sur une abstraction du système qui n'est plus en phase avec l'architecture matérielle des systèmes modernes. Etonnament compte tenu de son adoption, Python n'est pas nativement un langage multi-thread. L'article suivant vous apportera plus d'information à ce sujet. Mais la notion de multi-threading est elle-même discutable, comme l'explique ce blog. Il me semble qu'elle a d'abord été introduite en Java, pour recréer un traitement concurrent avec mémoire partagée au niveau de la machine virtuelle (sans nécessiter de fork de manière donc indépendante et portable duu système) et que les nouveaux langages ou les fonctionnalités ajoutées aux langages existants cherchent à s'affranchir de ce concept en construisant de nouveaux concepts comme les channels en go ou les streams en Java.

Développements futurs

Les zones d'amélioration sont donc encore nombreuses :

Les systèmes non distribuées ne sont pas déterministes Le test, debuggage, la compréhension et la reproduction des conditions de panne sont difficiles. De nouveaux outils et de nouvelles méthodes sont nécessaires.

Les grands systèmes distribués en production ne peuvent pas être arrêtés. Toutes les opérations de maintenace, de la sauvegarde à la mise à jour, doivent être conçus pour que les administrateurs puissent les exécuter sans arrêter le système. Au cours de ces opérations, le système se trouve donc dans un état intermédiaire partiellement défini du point de vue du temps mais bien défini à un niveau logique. Les algorithmes de snapshot distribué comme l'algorithme Chandy-Lamport montrent la nature locale du temps dans les systèmes distribués.

Les applications monolithiques étaient complexes à faire évoluer du fait de leur intrication. Les architectures distribués à base de micro services sont complexes à versionner, et nécessitent un outil garantissant une cohérence globale de l'état de configuration du système.

Pour conclure, la formation aux algorithmes est encore très concentrée sur des algorithmes séquentielles et la complexité, avec peu ou pas de matériel concernant les programmes distribués. Comment la nouvelle génération de développeurs sera-t-elle formée à la fois à ces questions de complexité qui restent importantes et aux problématiques propres aux algorithmes distribués ? Une formation au système distribué sera-t-elle nécessaire si les frameworks parviennent à en masquer la complexité ?