Introduction au 
lock-free programming 
avec std::atomics 
Meetup C++ 
25 novembre 2014 
Montpellier 
Cyril Comparon
Multi-tasking 
Concurrence 
Le système est composé de plusieurs tâches s’exécutant de manière (partiellement) 
indépendant...
Multi-tasking 
Multi-threading 
Les différentes tâches sont des exécutions distinctes du même programme, partageant le mêm...
Multi-tasking 
Multi-threading 
Les différentes tâches sont des exécutions distinctes du même programme, partageant le mêm...
Multi-tasking 
Thread-safety 
Une structure de donnée partagée est thread-safe si elle garantit un fonctionnement prédicti...
Compteur non thread-safe 
class Count 
{ 
public: 
Count() 
: m_val(0) 
{} 
int value() const 
{ 
return m_val; 
} 
void a...
Compteur thread-safe avec un mutex 
#include <mutex> 
class Count 
{ 
public: 
Count() : m_val(0) {} 
int value() const 
{...
principales opérations atomiques de std::atomics 
En C++11, principalement trois opérations atomiques ont été standardisée...
Compteur thread-safe avec un mutex 
#include <mutex> 
class Count 
{ 
public: 
Count() : m_val(0) {} 
int value() const 
{...
Compteur lock-free 
#include <atomic> 
class Count 
{ 
public: 
Count() : m_val(0) {} 
int value() const 
{ 
return m_val;...
Compteur lock-free 
#include <atomic> 
class Count 
{ 
public: 
Count() : m_val(0) {} 
int value() const 
{ 
return m_val;...
std::atomics<T> supporte n’importe quel type 
T std::atomic::exchange(T desired) 
{ 
std::lock_guard<std::mutex> lock(m_mu...
Conclusion 
Mutual exclusion 
Atomic operations
Questions en vrac 
Pourquoi tu me dis que c’est compliqué alors que ça a l’air tout simple ? 
Pourquoi je n’envoie pas tou...
Les prochaines fois 
- Bas niveau : 
• Pourquoi ça ne devrait pas marcher ? 
 Weak cache coherency, out-of-order executio...
Remerciements 
CppReference 
http://en.cppreference.com/w/cpp/atomic 
Herb Sutter – atomic<> Weapons (2012) 
http://channe...
Remerciements 
CppReference 
http://en.cppreference.com/w/cpp/atomic 
Herb Sutter – atomic<> Weapons (2012) 
http://channe...
Prochain SlideShare
Chargement dans…5
×

Introduction au lock-free programming avec std::atomics

522 vues

Publié le

Introduction à la programmation lock-free en C++11 au moyen des atomics. Présentation données par Cyril Comparon lors des rencontres C++ de Montpellier (novembre 2014).

Publié dans : Logiciels
0 commentaire
0 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

Aucun téléchargement
Vues
Nombre de vues
522
Sur SlideShare
0
Issues des intégrations
0
Intégrations
11
Actions
Partages
0
Téléchargements
9
Commentaires
0
J’aime
0
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive
  • Me présenter
  • Piqûre de rappel – multi-tasking - système concurrent - parallélisme
  • Vieux mono-CPU – scheduler de l’OS – (preemptive multitasking) illusion de parallélisme – multithreading déjà très utilisé
  • Parallélisme induit par le multi-CPU
    Multithreading rendu encore plus important à cause de cela
  • Patterns proposés par wikipedia
    Lock-based = critical sections = blocking algos
    Lock-free = non-blocking algos
  • Que va-t-il se passer et pourquoi ?
    Race condition ! Dépend de la température de la pièce !
  • Ca marche ! Mais performances bof et surtout, que se passe-t-il si l’opération n’est pas triviale, fait des I/O ou a d’autres dépendances ?
    On peut le simuler en suspendant un thread.
  • Ca marche ! Mais performances bof et surtout, que se passe-t-il si l’opération n’est pas triviale, fait des I/O ou a d’autres dépendances ?
    On peut le simuler en suspendant un thread.
  • Obstruction-free ! = même en suspendant un des deux threads, l’autre thread finit son boulot !
    (c’est une des propriétés des algorithmes lock-free)
  • Obstruction-free ! = même en suspendant un des deux threads, l’autre thread finit son boulot !
    (c’est une des propriétés des algorithmes lock-free)
  • std::atomic<T>::is_lock_free() renseigne si T est supporté sans lock sur le hardware cible.
    Sur les machines les plus courantes, les entiers 32 et 64 bits et les pointeurs.
  • Lock-based = critical sections = blocking algos = simpler but can kill scalability and induce weird behaviors (priority inversion!)
    Lock-free = non-blocking algos = eliminate deadlocks = eliminate/reduce blocking waiting = concurrency++ = scalability++
  • Parce que le compilateur et le processeur et ses caches ont leurs raisons
    N’adresse pas le même genre de problème – embarrassingly parallel problems
    Parce que ça ne fait pas de mal de le savoir quand même, et parce que si déjà on fait du C++, c’est qu’on est un peu maso et qu’on cherche à tirer le max de perf/watt.
    De toute façon les techniques que vous allez apprendre et notamment la pensée « transactionnelle » est précieuse dans les environnements distribués bien au-delà du multithreading !
    Oui, beaucoup, c’est un domaine très actif de la recherche. Plus prosaïquement, ce sont les briques de base sans lesquelles vous n’auriez meme pas vos mutexes.
    Non, il y a meme une API dédiée dans la lib java, c’est dire !
  • Introduction au lock-free programming avec std::atomics

    1. 1. Introduction au lock-free programming avec std::atomics Meetup C++ 25 novembre 2014 Montpellier Cyril Comparon
    2. 2. Multi-tasking Concurrence Le système est composé de plusieurs tâches s’exécutant de manière (partiellement) indépendante. Parallélisme Ces tâches peuvent en plus s’exécuter au même moment. Cyril Comparon – Meetup C++ Montpellier 25/11/2014
    3. 3. Multi-tasking Multi-threading Les différentes tâches sont des exécutions distinctes du même programme, partageant le même espace mémoire. thread1 thread2 Cyril Comparon – Meetup C++ Montpellier 25/11/2014
    4. 4. Multi-tasking Multi-threading Les différentes tâches sont des exécutions distinctes du même programme, partageant le même espace mémoire. thread1 thread2 Cyril Comparon – Meetup C++ Montpellier 25/11/2014
    5. 5. Multi-tasking Thread-safety Une structure de donnée partagée est thread-safe si elle garantit un fonctionnement prédictible et reste dans un état valide (non corrompu) quel que soit l’ordre d’appel de ses méthodes. Plusieurs implémentations possibles: - Re-entrancy, thread-local storage, immutable objects  outils/work-arounds - Mutual exclusion - Atomic operations Cyril Comparon – Meetup C++ Montpellier 25/11/2014
    6. 6. Compteur non thread-safe class Count { public: Count() : m_val(0) {} int value() const { return m_val; } void add(int v) { m_val += v; } private: int m_val; }; #include <iostream> #include <future> Count count; void incrementer() { for(int i=0; i<20000000; i++) count.add(1); } void decrementer() { for(int i=0; i<10000000; i++) count.add(-2); } void main() { std::future<void> future1(std::async(incrementer)); std::future<void> future2(std::async(decrementer)); future1.wait(); future2.wait(); std::cout << "Val = " << count.value() << std::endl; }
    7. 7. Compteur thread-safe avec un mutex #include <mutex> class Count { public: Count() : m_val(0) {} int value() const { std::lock_guard<std::mutex> lock(m_mutex); return m_val; } void add(int v) { std::lock_guard<std::mutex> lock(m_mutex); m_val += v; } private: mutable std::mutex m_mutex; int m_val; }; #include <iostream> #include <future> Count count; void incrementer() { for(int i=0; i<20000000; i++) count.add(1); } void decrementer() { for(int i=0; i<10000000; i++) count.add(-2); } void main() { std::future<void> future1(std::async(incrementer)); std::future<void> future2(std::async(decrementer)); future1.wait(); future2.wait(); std::cout << "Val = " << count.value() << std::endl; }
    8. 8. principales opérations atomiques de std::atomics En C++11, principalement trois opérations atomiques ont été standardisées, car le plus souvent supportées par le hardware : int exchange(int desired) Cyril Comparon – Meetup C++ Montpellier 25/11/2014 Atomically replaces the underlying value with desired. The operation is read-modify-write operation. bool compare_exchange(int &expected, int desired) a.k.a CAS Atomically compares the value stored in *this with the value pointed to by expected, and if those are equal, replaces the former with desired (performs read-modify- write operation). Otherwise, loads the actual value stored in *this into *expected (performs load operation). int fetch_add(int incr) Atomically replaces the current value with the result of arithmetic addition of the value and incr. The operation is read-modify-write operation.
    9. 9. Compteur thread-safe avec un mutex #include <mutex> class Count { public: Count() : m_val(0) {} int value() const { std::lock_guard<std::mutex> lock(m_mutex); return m_val; } void add(int v) { std::lock_guard<std::mutex> lock(m_mutex); m_val += v; } private: mutable std::mutex m_mutex; int m_val; }; #include <iostream> #include <future> Count count; void incrementer() { for(int i=0; i<20000000; i++) count.add(1); } void decrementer() { for(int i=0; i<10000000; i++) count.add(-2); } void main() { std::future<void> future1(std::async(incrementer)); std::future<void> future2(std::async(decrementer)); future1.wait(); future2.wait(); std::cout << "Val = " << count.value() << std::endl; }
    10. 10. Compteur lock-free #include <atomic> class Count { public: Count() : m_val(0) {} int value() const { return m_val; } void add(int v) { m_val.fetch_add(v); } private: std::atomic<int> m_val; }; #include <iostream> #include <future> Count count; void incrementer() { for(int i=0; i<20000000; i++) count.add(1); } void decrementer() { for(int i=0; i<10000000; i++) count.add(-2); } void main() { std::future<void> future1(std::async(incrementer)); std::future<void> future2(std::async(decrementer)); future1.wait(); future2.wait(); std::cout << "Val = " << count.value() << std::endl; }
    11. 11. Compteur lock-free #include <atomic> class Count { public: Count() : m_val(0) {} int value() const { return m_val; } void add(int v) { m_val.fetch_add(v); } private: std::atomic<int> m_val; }; #include <iostream> #include <future> Count count; void incrementer() { for(int i=0; i<20000000; i++) count.add(1); std::cout << "Incrementer finished!" << std::endl; } void decrementer() { for(int i=0; i<10000000; i++) count.add(-2); std::cout << “Decrementer finished!" << std::endl; } void main() { std::future<void> future1(std::async(incrementer)); std::future<void> future2(std::async(decrementer)); future1.wait(); future2.wait(); std::cout << "Val = " << count.value() << std::endl; }
    12. 12. std::atomics<T> supporte n’importe quel type T std::atomic::exchange(T desired) { std::lock_guard<std::mutex> lock(m_mutex); T ret = m_value; m_value = desired; return ret; } bool std::atomic::compare_exchange(T &expected, T desired) { std::lock_guard<std::mutex> lock(m_mutex); if(m_value == expected) { m_value = desired; return true; } else { expected = m_value; return false; } } T std::atomic::fetch_add(T incr) { std::lock_guard<std::mutex> lock(m_mutex); T ret = m_value; m_value += incr; return ret; } Cyril Comparon – Meetup C++ Montpellier 25/11/2014
    13. 13. Conclusion Mutual exclusion Atomic operations
    14. 14. Questions en vrac Pourquoi tu me dis que c’est compliqué alors que ça a l’air tout simple ? Pourquoi je n’envoie pas tout sur mon GPU qui est vachement plus puissant que mon CPU et il paraît que tout le monde fait que ça maintenant ? Pourquoi on s’embête avec tout ça alors qu’il existe des langages et des outils de plus haut niveau qui me cachent toute la complexité ? Y a-t-il des application pratiques intéressantes ? Est-ce spécifique au C++ ? Autres questions ? Cyril Comparon – Meetup C++ Montpellier 25/11/2014
    15. 15. Les prochaines fois - Bas niveau : • Pourquoi ça ne devrait pas marcher ?  Weak cache coherency, out-of-order execution • Pourquoi ça marche quand même ?  Memory ordering / memory barriers - Le C++11 memory model - Penser en termes de transactions (ACID) - Algorithmes lock-free un peu plus intéressants - Différents niveaux de lock-freedom - Le problème ABA Cyril Comparon – Meetup C++ Montpellier 25/11/2014
    16. 16. Remerciements CppReference http://en.cppreference.com/w/cpp/atomic Herb Sutter – atomic<> Weapons (2012) http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2 Herb Sutter – Lock-Free Programming http://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Lock-Free-Programming-or-Juggling-Razor-Blades-Part-I The Concurrency Kit http://concurrencykit.org Wikipedia http://en.wikipedia.org Cyril Comparon – Meetup C++ Montpellier 25/11/2014
    17. 17. Remerciements CppReference http://en.cppreference.com/w/cpp/atomic Herb Sutter – atomic<> Weapons (2012) http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2 Herb Sutter – Lock-Free Programming http://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Lock-Free-Programming-or-Juggling-Razor-Blades-Part-I The Concurrency Kit http://concurrencykit.org Wikipedia http://en.wikipedia.org Cyril Comparon – Meetup C++ Montpellier 25/11/2014

    ×