URL: https://linuxfr.org/news/ibm-lance-la-memoire-transactionnelle-dans-le-materiel Title: IBM lance la mémoire transactionnelle dans le matériel Authors: claudex Date: 2011年09月02日T14:51:47+02:00 License: CC By-SA Tags: matériel, transaction, mémoire, technologie, implémentation_matérielle, concurrence et ibm Score: 45 Le supercalculateur Sequoia (prévu pour être le plus puissant supercalculateur lors de sa sortie) ne fera pas que battre des records de FLOPS, il utilisera aussi des processeurs BlueGene/Q d’IBM, les premiers processeurs commerciaux à utiliser une mémoire transactionnelle matérielle. Le processeur développé par Sun et annulé avec le rachat par Oracle, aurait également dû le prendre en charge. C’est l’occasion d’expliquer ce qu’est la mémoire transactionnelle : une technique peu connue car elle pose des problèmes de performance lorsque plusieurs processus ou fils d’exécution (_threads_) doivent accéder à une valeur partagée. N. D. A. : _Merci à [Nÿco](https://linuxfr.org/users/nyco), [NeoX](https://linuxfr.org/users/neox) et [Michel Barret](https://linuxfr.org/users/barmic) pour leur aide lors de la rédaction de cette dépêche._ ---- [La mémoire transactionnelle logicielle](http://fr.wikipedia.org/wiki/M%C3%A9moire_transactionnelle_logicielle) [Article d’Ars Technica sur le BlueGene/Q](http://arstechnica.com/hardware/news/2011/08/ibms-new-transactional-memory-make-or-break-time-for-multithreaded-revolution.ars) ---- ##Le problème du multithread Le _multithread_ permet souvent d’améliorer les performances en exécutant plusieurs opérations en même temps. Cependant, à part quelques algorithmes qui peuvent s’exécuter en parallèle de manière totalement indépendante, il est souvent nécessaire de partager des ressources, comme une valeur, entre les différents _threads_. Et il faut absolument éviter qu’un _thread_ lise une valeur obsolète ou incohérente. Prenons l’exemple d’une mise à jour de compte en banque. Un _thread_ va retirer de l’argent et un autre _thread_ va en ajouter, tout ça sur le même compte. Les deux _threads_ lisent la valeur du montant du compte. Ensuite, le _thread_ qui ajoute de l’argent s’exécute plus vite (il y a plein de raisons possibles pour ça), il met à jour la valeur du compte par la nouvelle valeur qu’il a calculé (montant + valeur qu’il a ajoutée). Cependant, lorsque l’autre _thread_ se termine, il met à jour la valeur, il écrit dans la mémoire la valeur qu’il a calculé (le montant *lors de la lecture* - la valeur qu’il retire), la valeur dans la mémoire ne tient donc pas compte de l’ajout qui a été fait. ##Les mutex Pour régler ce problème, on utilise, la plupart du temps, des _mutex_ (_mutual exclusion_ ou exclusion mutuelle). Le programmeur détermine quelles sont les zones sensibles, au début de celles‐ci, le programme demande l’acquisition du _mutex_, et si personne ne l’a demandé, il l’obtient et peut exécuter la suite du code. Quand il a fini, il libère le _mutex_. Si un _thread_ a déjà le _mutex_, celui qui le demande est mis en attente jusqu’à ce qu’il soit libéré. Cette technique permet d’être sûr que le _thread_ est le seul à utiliser la ressource partagée. Cependant, elle pose d’autres problèmes. Premièrement, il faut être sûr de relâcher le _mutex_ quand on a fini de l’utiliser, sous peine de bloquer tous les autres _threads_. Deuxièmement, il y a un risque de blocage complet quand on utilise plusieurs mutex. Par exemple, prenons deux _threads_ avec deux mutex A et B (chacun pour une ressource partagée) ; le premier _thread_ prend le _mutex_ A, le second _thread_ prend le _mutex_ B. Le premier _thread_ veut ensuite prendre le _mutex_ B (sans avoir relâché le _mutex_ A) et le second _thread_ veut prendre le mutex A, on se retrouve dans une situation où les deux _threads_ vont attendre indéfiniment que les _mutex_ soient relâchés, sans que cela se produise. Cela n’arriverait pas si les _threads_ prenaient les mutex dans le même ordre (d’abord le A puis le B), mais ce n’est pas toujours facile à mettre en place, ni même possible. ##Les transactions Pour résoudre les problèmes, on peut utiliser la mémoire transactionnelle. Elle peut être implémentée de manière logicielle ou matérielle. La technique est similaire à celle utilisée dans les transactions des bases de données. La méthode est optimiste, les _threads_ s’exécutent sans verrou, et à la fin de l’opération (le _commit_), on vérifie que les données n’ont pas été modifiées par d’autres _threads_ pendant le traitement, sinon, on recommence. On peut l’exprimer dans le code de la manière suivante, le bloc `« atomic »` définit la transaction : ```text // Insère atomiquenent un nœud dans une liste doublement liée atomic { newNode→prev = node; newNode→next = node→next; node→next→prev = newNode; node→next = newNode; } ``` Il y a un surcoût en mémoire et en temps processeur, car les données doivent être dupliquées pour chaque transaction et des instructions sont parfois exécutées dans le vide, puisque la transaction échouera si les données sont modifiées. D’après les défenseur des transactions, le surcoût processeur est récupéré par l’absence de gestion des _mutex_, qui n’est pas une opération négligeable. Dans le cas du BlueGene/Q, le surcoût en mémoire sera absorbé par les caches des processeurs, par contre, le surcoût processeur sera toujours présent. Des [implémentations logicielles](http://en.wikipedia.org/wiki/Software_transactional_memory#Implementations) existent pour beaucoup de langages, dont C++, Java, Python, Scala, Perl...