Top Banner
Introduction au lock - free programming avec std :: atomics Meetup C++ 25 novembre 2014 Montpellier Cyril Comparon
17

Introduction au lock-free programming avec std::atomics

Jul 15, 2015

Download

Software

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Introduction au lock-free programming avec std::atomics

Introduction au

lock-free programmingavec std::atomics

Meetup C++25 novembre 2014Montpellier

Cyril Comparon

Page 2: Introduction au lock-free programming avec std::atomics

Multi-tasking

ConcurrenceLe système est composé de plusieurs tâches s’exécutant de manière (partiellement) indépendante.

ParallélismeCes tâches peuvent en plus s’exécuter au même moment.

Cyril Comparon – Meetup C++ Montpellier 25/11/2014

Page 3: Introduction au lock-free programming avec std::atomics

Multi-tasking

Multi-threadingLes 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

Page 4: Introduction au lock-free programming avec std::atomics

Multi-tasking

Multi-threadingLes 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

Page 5: Introduction au lock-free programming avec std::atomics

Multi-tasking

Thread-safetyUne 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

Page 6: Introduction au lock-free programming avec std::atomics

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;

}

Page 7: Introduction au lock-free programming avec std::atomics

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;

}

Page 8: Introduction au lock-free programming avec std::atomics

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 :

Cyril Comparon – Meetup C++ Montpellier 25/11/2014

int exchange(int desired)Atomically replaces the underlying value with desired. The operation is read-modify-write operation.

bool compare_exchange(int &expected,

int desired)

a.k.a CASAtomically 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.

Page 9: Introduction au lock-free programming avec std::atomics

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;

}

Page 10: Introduction au lock-free programming avec std::atomics

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;

}

Page 11: Introduction au lock-free programming avec std::atomics

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;

}

Page 12: Introduction au lock-free programming avec std::atomics

std::atomics<T> supporte n’importe quel type

Cyril Comparon – Meetup C++ Montpellier 25/11/2014

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;

}

Page 13: Introduction au lock-free programming avec std::atomics

Conclusion

Mutual exclusion

Atomic operations

Page 14: Introduction au lock-free programming avec std::atomics

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

Page 15: Introduction au lock-free programming avec std::atomics

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

Page 16: Introduction au lock-free programming avec std::atomics

Remerciements

CppReferencehttp://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 Programminghttp://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Lock-Free-Programming-or-Juggling-Razor-Blades-Part-I

The Concurrency Kithttp://concurrencykit.org

Wikipediahttp://en.wikipedia.org

Cyril Comparon – Meetup C++ Montpellier 25/11/2014

Page 17: Introduction au lock-free programming avec std::atomics

Remerciements

CppReferencehttp://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 Programminghttp://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Lock-Free-Programming-or-Juggling-Razor-Blades-Part-I

The Concurrency Kithttp://concurrencykit.org

Wikipediahttp://en.wikipedia.org

Cyril Comparon – Meetup C++ Montpellier 25/11/2014