Initialisation dynamique en C++11
(n2660)
Daniel Strul - 19/12/2015
Disclaimer
• Vous n'allez rien apprendre d'utile ☺
– En C++03, les variables globales c'est le mal...
– En C++11, c'est un...
Le problème des
variables statiques non-locales
Les variables statiques non-locales
• Ce sont
– Les variables globales
– Les variables membres statiques
• Initialisation
...
Compter jusqu'à 2 avec des variables statiques...
• Si on est chanceux, ça marche...
• Mais si on n'a pas de chance...
Compter jusqu'à 2... ou pas!
Qu'est-ce qui se passe?
Initialisation
Ordre des
initialisations
Un.c puis Deux.c Deux.c puis Un.c
1ère initialisation UN ...
Le compilateur est buggué?
Objects with static storage duration defined in
namespace scope in the same translation unit
an...
C'est le static initialization order fiasco (SIOF)
What’s the “static initialization order fiasco”?
A subtle way to crash ...
Qu'a changé C++11?
Static-duration object initialization may constitute a large
fraction of process time, particularly whe...
En C++11, le SIOF se parallélise
➢ Dans un même fichier source: inchangé (sauf cas particuliers)
➢ Fichiers distincts en m...
La solution c'est le singleton!?
Move each non-local static object into its own
function, where it's declared static.
Thes...
Le problème du singleton
Les variables statiques locales
• Ce sont
– Les statiques définies dans une fonction / un bloc
• Initialisation
– Au 1er p...
Deux variantes possibles du singleton
Singleton retournant
une référence
Singleton retournant
un pointeur
➢ Il y a de nomb...
Test d'utilisation concurrente d'un singleton
avec gcc (avant les magic statics)
Le singleton se démultiplie...
Sans lock, l'initialisation du singleton n'est pas thread-safe...
... on peut le construire...
Test d'utilisation concurrente d'un singleton
avec Visual Studio
Le singleton se divise...
Sans lock, l'initialisation du singleton n'est pas thread-safe...
... on peut en recevoir un en ...
Test d'utilisation concurrente d'un singleton
retournant un pointeur
Le singleton à pointeur s'atomise...
Sans lock, l'initialisation du singleton n'est pas thread-safe...
... on peut obtenir...
Le singleton c'est pas la solution?
Avoid using a lazily-initialized Singleton unless
you really need it.
Use eager initia...
3 états, 2 valeurs, 1 problème
Etat de la variable statique Etat observable
Avant initialisation Pas encore initialisé
Pen...
Le problème du mutexage du singleton
Exemples de singleton mutexé
Singleton retournant
une référence
Singleton retournant
un pointeur
Singleton mutexé et performances
Visual Studio 2013 / x86 gcc 4.9.3 / cygwin x64
sans magic statics
Sans mutex 100 ms 60 m...
Singleton mutexé et efficacité
Gestion du mutex pour 10 millions d'appels à getInstance()
➢ Le mutex n'est utile que penda...
Le Double-Checked Locking Pattern (DCLP)
Every call to instance must acquire and release the
lock.
Although this implement...
Principe du Double-Checked Locking Pattern
• On vérifie si le singleton est déjà initialisé
1. S'il ne l'est pas, on verro...
Mais DCLP est cassé (Java 1.4) ☹
• Double-Checked Locking is widely cited and used as an
efficient method for implementing...
En C++03 aussi, DCLP est cassé ☹
DCLP is designed to add efficient thread-safety to initialization of
a shared resource (s...
Qu'est-ce qui ne marche pas?
1
2
3
4
5
➢ DCLP repose sur un ordre d'exécution très précis
➢ Mais qu'est-ce qui se passe si...
Compilation et ordre d'exécution
➢ Conforming implementations are required to
emulate (only) the observable behavior of th...
Architecture et ordre d'exécution
➢ Les opérations peuvent être réordonnées selon l'architecture
➢ L'architecture peut réo...
Réparer DCLP avec des barrières... ☹
• To use the DCLP Optimization correctly on some platforms,
CPU-specific instructions...
Singleton et DCLP en C++11
Ce que change C++11
• Concurrency introduces potential deadlock or data races in
the dynamic initialization and destructio...
Les magic statics
➢ L'initialisation des variables statiques locales devient thread-safe
➢ Implémenté sous gcc depuis gcc ...
3 états, 2 valeurs, 1 solution
Etat de la variable statique Etat observable
Avant initialisation Pas encore initialisé
Pen...
Le singleton devient thread-safe
Singleton retournant
une référence
Singleton retournant
un pointeur
➢ La thread-safety de...
Sous le capot: les magic statics de gcc
➢ C'est DCLP en assembleur
➢ Le compilateur sait implémenter au mieux selon l'arch...
En C++03, on pouvait réparer DCLP avec Boost ou TBB
➢ Les libraries fournissaient des objets atomiques
➢ Les libraries gér...
En C++11, les objets atomiques intègrent le langage
➢ La syntaxe C++11 est un peu plus lourde que pour Boost ou TBB
➢ L'or...
Les atomiques C++11 permettent des optimisations fines
➢ Ce type de fine-tuning est à utiliser avec précaution
Jeff Preshi...
Singleton, thread-safety et performances
Durée d'exécution pour 100 millions d'appels à getInstance()
➢ Les statiques loca...
Qu'est-ce que ça change
pour le développement multi-cible?
Environnement
Windows
Environnement Linux
Statiques locales Thr...
DCLP sert encore à quelque chose?
➢ Le singleton est un cas particulier de l'idiome d'initialisation tardive (lazy)
➢ Les ...
DCLP dans le langage avec std::call_once
➢std::call_once encapsule l'appel à une fonction
➢ La fonction ne sera appelée qu...
Conclusions
Qu'est-ce qui change en C++11?
• Initialisation dynamique des statiques non-locales
– Pas de changement majeur en monothre...
Disclaimer
• Vous n'avez rien appris de très utile... ☺
• Mais quand même...
– Pensez au static initialization order fiasc...
Prochain SlideShare
Chargement dans…5
×

Initialisation dynamique en c++11 (n2660)

195 vues

Publié le

Support de formation sur les évolutions en C++11 concernant:
- L'initialisation dynamique des variables statiques non-locales (le "static initialisation order fiasco")
- La thread-safety du singleton via les "magic statics"
- Le thread-safety du Double-Checked Locking Pattern via les atomiques

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
195
Sur SlideShare
0
Issues des intégrations
0
Intégrations
2
Actions
Partages
0
Téléchargements
8
Commentaires
0
J’aime
0
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Initialisation dynamique en c++11 (n2660)

  1. 1. Initialisation dynamique en C++11 (n2660) Daniel Strul - 19/12/2015
  2. 2. Disclaimer • Vous n'allez rien apprendre d'utile ☺ – En C++03, les variables globales c'est le mal... – En C++11, c'est un peu mieux... • Au menu – Le static initialization order fiasco (SIOF) – Le problème du singleton en C++03 – Le double-checked locking pattern (DCLP) – Les magic statics gcc et C++11
  3. 3. Le problème des variables statiques non-locales
  4. 4. Les variables statiques non-locales • Ce sont – Les variables globales – Les variables membres statiques • Initialisation – Statique: à la compilation – Dynamique: au lancement du programme
  5. 5. Compter jusqu'à 2 avec des variables statiques...
  6. 6. • Si on est chanceux, ça marche... • Mais si on n'a pas de chance... Compter jusqu'à 2... ou pas!
  7. 7. Qu'est-ce qui se passe? Initialisation Ordre des initialisations Un.c puis Deux.c Deux.c puis Un.c 1ère initialisation UN = abs(1) = 1 DEUX = UN +1 = 0 + 1 = 1 2ème initialisation DEUX = UN + 1 = 1+ 1 = 2 UN = abs(1) = 1 ➢ L'ordre d'initialisation des variables est indéterminé
  8. 8. Le compilateur est buggué? Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit. ➢ Pour des variables statiques non-locales définies dans un même fichier source, l'initialisation dynamique se fait dans l'ordre ➢ Aucune contrainte si les variables sont définies dans des fichiers séparés C++03, clause 3.6.2, "Initialization of non-local objects"
  9. 9. C'est le static initialization order fiasco (SIOF) What’s the “static initialization order fiasco”? A subtle way to crash your program. Suppose you have two static objects x and y in separate source files, say x.cpp and y.cpp. Suppose that the initialization for y calls some method on x. That’s it. It’s that simple. You have a 50%-50% chance of dying. Marshall Cline, Bjarne Stroustrup et al., "C++ Super-FAQ"
  10. 10. Qu'a changé C++11? Static-duration object initialization may constitute a large fraction of process time, particularly when users are waiting for program start, and hence may benefit from parallel execution. In the sequential 2003 standard, unspecified order of initialization essentially required some serial order of initialization. In the parallel proposed standard, the unspecified order of initialization admits concurrent initialization. L. Crowl, n2660, "Dynamic Initialization and Destruction with Concurrency" ➢ C++11 parallélise l'initialisation pour accéler le lancement des programmes
  11. 11. En C++11, le SIOF se parallélise ➢ Dans un même fichier source: inchangé (sauf cas particuliers) ➢ Fichiers distincts en monothread: inchangée (ordre indéterminé) ➢ Fichiers distincts en multithread: initialisation concurrente possible Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit. If a program starts a thread, the subsequent initialization of a variable is unsequenced with respect to the initialization of a variable defined in a different translation unit. Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of a variable defined in a different translation unit. C++11 clause 3.6.2, "Initialization of non-local variables"
  12. 12. La solution c'est le singleton!? Move each non-local static object into its own function, where it's declared static. These functions return references to the objects they contain. In other words, non-local static objects are replaced with local static objects. Aficionados of design patterns will recognize this as a common implementation of the Singleton pattern. Scott Meyers, "Effective C++, 3rd ed.", 2005 ➢ Le singleton est la solution la plus courante au SIOF ➢ Il repose sur l'initialisation dynamique des variables statiques locales
  13. 13. Le problème du singleton
  14. 14. Les variables statiques locales • Ce sont – Les statiques définies dans une fonction / un bloc • Initialisation – Au 1er passage dans la fonction / le bloc
  15. 15. Deux variantes possibles du singleton Singleton retournant une référence Singleton retournant un pointeur ➢ Il y a de nombreuses autres variantes ➢ On trouve également des templates de singletons
  16. 16. Test d'utilisation concurrente d'un singleton avec gcc (avant les magic statics)
  17. 17. Le singleton se démultiplie... Sans lock, l'initialisation du singleton n'est pas thread-safe... ... on peut le construire plusieurs fois ou en fabriquer plusieurs
  18. 18. Test d'utilisation concurrente d'un singleton avec Visual Studio
  19. 19. Le singleton se divise... Sans lock, l'initialisation du singleton n'est pas thread-safe... ... on peut en recevoir un en cours de construction
  20. 20. Test d'utilisation concurrente d'un singleton retournant un pointeur
  21. 21. Le singleton à pointeur s'atomise... Sans lock, l'initialisation du singleton n'est pas thread-safe... ... on peut obtenir un pointeur nul ... on pourrait obtenir un pointeur vers un objet en construction ... on pourrait obtenir plusieurs pointeurs différents
  22. 22. Le singleton c'est pas la solution? Avoid using a lazily-initialized Singleton unless you really need it. Use eager initialization instead, i.e., initialize a resource at the beginning of the program run. Initializing a singleton resource during single- threaded program startup is the simplest way to offer fast, thread-safe singleton access. S. Meyers et A. Alexandrescu, "C++ and the Perils of Double-Checked Locking", 2004 ➢ Meyers recommandait le singleton comme solution au SIOF ➢ Mais en multithread, le singleton pose de nouveaux problèmes
  23. 23. 3 états, 2 valeurs, 1 problème Etat de la variable statique Etat observable Avant initialisation Pas encore initialisé Pendant l'initialisation Veuillez patienter??? Après initialisation Initialisé ➢ Il faut un troisième état, "Initialisation en cours, veuiller patienter"... ➢ Bref, il faut un mutex
  24. 24. Le problème du mutexage du singleton
  25. 25. Exemples de singleton mutexé Singleton retournant une référence Singleton retournant un pointeur
  26. 26. Singleton mutexé et performances Visual Studio 2013 / x86 gcc 4.9.3 / cygwin x64 sans magic statics Sans mutex 100 ms 60 ms Avec mutex 6.3 s 11.5 s Durée d'exécution pour 100 millions d'appels à getInstance() ➢ Le mutexage du singleton, ça n'est pas vraiment gratuit ☹
  27. 27. Singleton mutexé et efficacité Gestion du mutex pour 10 millions d'appels à getInstance() ➢ Le mutex n'est utile que pendant la phase d'initialisation ➢ Après, il ne sert à rien, mais on doit le verrouiller quand même ☹ Itération Comportement 1 Verrouillage du mutex pour construction thread-safe 2 Verrouillage du mutex parce qu'il est là n°1 3 Verrouillage du mutex parce qu'il est là n°2 ... 10 000 000 Verrouillage du mutex parce qu'il est là n°9 999 999
  28. 28. Le Double-Checked Locking Pattern (DCLP) Every call to instance must acquire and release the lock. Although this implementation is now thread-safe, the overhead from the excessive locking may be unacceptable. A better way to solve this problem is to use Double- Checked Locking, which is a pattern for optimizing away unnecessary locking. Douglas C. Schmidt, "Double-Checked Locking, An Optimization Pattern for Efficiently Initializing and Accessing Thread-safe Objects", 1997 ➢ Le DCLP vise à ne prendre le mutex que quand c'est nécessaire
  29. 29. Principe du Double-Checked Locking Pattern • On vérifie si le singleton est déjà initialisé 1. S'il ne l'est pas, on verrouille le mutex 2. On vérifie que le singleton n'a pas été initialisé pendant qu'on attendait le lock 3. Si personne n'a initialisé le singleton dans l'intervalle, on l'initialise
  30. 30. Mais DCLP est cassé (Java 1.4) ☹ • Double-Checked Locking is widely cited and used as an efficient method for implementing lazy initialization in a multithreaded environment. • It doesn't work • There are lots of reasons it doesn't work. • After understanding those, you may be tempted to try to devise a way to "fix" the double-checked locking idiom. • Your fixes will not work: there are more subtle reasons why your fix won't work. • Understand those reasons, come up with a better fix, and it still won't work, because there are even more subtle reasons. David Bacon et coll., The "Double-Checked Locking is Broken" Declaration, 2000
  31. 31. En C++03 aussi, DCLP est cassé ☹ DCLP is designed to add efficient thread-safety to initialization of a shared resource (such as a Singleton), but it has a problem: it’s not reliable. Furthermore, there’s virtually no portable way to make it reliable in C++ (or in C) without substantively modifying the conventional pattern implementation. To make matters even more interesting, DCLP can fail for different reasons on uniprocessor and multiprocessor architectures. S. Meyers et A. Alexandrescu, "C++ and the Perils of Double-Checked Locking", 2004
  32. 32. Qu'est-ce qui ne marche pas? 1 2 3 4 5 ➢ DCLP repose sur un ordre d'exécution très précis ➢ Mais qu'est-ce qui se passe si cet ordre n'est pas respecté?...
  33. 33. Compilation et ordre d'exécution ➢ Conforming implementations are required to emulate (only) the observable behavior of the [C++] abstract machine. ➢ This provision is sometimes called the “as-if” rule, because an implementation is free to disregard any requirement of this International Standard as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program. ➢ Un compilateur peut réordonner tant que le comportement est inchangé ➢ En C++03, le modèle d'exécution est mono-thread ➢ Le compilateur peut réordonner certaines opérations, et casser DCLP C++03, clause 1.9, "Program execution"
  34. 34. Architecture et ordre d'exécution ➢ Les opérations peuvent être réordonnées selon l'architecture ➢ L'architecture peut réordonner certaines opérations et casser DCLP P. E. McKenney, "Memory Barriers: a Hardware View for Software Hackers", 2009
  35. 35. Réparer DCLP avec des barrières... ☹ • To use the DCLP Optimization correctly on some platforms, CPU-specific instructions must be inserted. D. C. Schmidt, "Pattern-Oriented Software Architecture" Vol. 2, 2000
  36. 36. Singleton et DCLP en C++11
  37. 37. Ce que change C++11 • Concurrency introduces potential deadlock or data races in the dynamic initialization and destruction of static-duration objects. • The language must introduce new syntax, define synchronization, or limit programs. • This proposal breaks the problem into three reasonably separable parts: – initialization of function-local static-duration objects, – initialization of non-local static-duration objects, – and destruction of all static-duration objects. L. Crowl, n2148, "Dynamic Initialization and Destruction with Concurrency"
  38. 38. Les magic statics ➢ L'initialisation des variables statiques locales devient thread-safe ➢ Implémenté sous gcc depuis gcc 4.3 (a servi de base au standard) ➢ Implémenté sous Visual Studio dès VS 2015 [A variable with static or thread storage duration] is initialized the first time control passes through its declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. C++11 clause 6.7, "Declaration statement"
  39. 39. 3 états, 2 valeurs, 1 solution Etat de la variable statique Etat observable Avant initialisation Pas encore initialisé Pendant l'initialisation Veuillez patienter... Après initialisation Initialisé ➢ On a notre troisième état, "Initialisation en cours, veuiller patienter"
  40. 40. Le singleton devient thread-safe Singleton retournant une référence Singleton retournant un pointeur ➢ La thread-safety de l'initialisation est intégrée dans le langage ➢ Utiliser un mutex ou DCLP devient inutile
  41. 41. Sous le capot: les magic statics de gcc ➢ C'est DCLP en assembleur ➢ Le compilateur sait implémenter au mieux selon l'architecture if (initialized) return instance; acquire(initialisation guard); if (initialized) return instance; instance = new Singleton(); release(initialisation guard); return instance;
  42. 42. En C++03, on pouvait réparer DCLP avec Boost ou TBB ➢ Les libraries fournissaient des objets atomiques ➢ Les libraries géraient l'ordre d'exécution (opaque calls, assembleur...)
  43. 43. En C++11, les objets atomiques intègrent le langage ➢ La syntaxe C++11 est un peu plus lourde que pour Boost ou TBB ➢ L'ordre d'exécution est garanti par le langage (memory model)
  44. 44. Les atomiques C++11 permettent des optimisations fines ➢ Ce type de fine-tuning est à utiliser avec précaution Jeff Preshing, "Double-Checked Locking is Fixed In C++11"
  45. 45. Singleton, thread-safety et performances Durée d'exécution pour 100 millions d'appels à getInstance() ➢ Les statiques locales C++11 sont thread-safe et efficaces ➢ Implémenter DCLP pour un singleton est inutile Thread-safe? Visual Studio 2013 x86 gcc 4.9.3 cygwin x64 statique C++03 NON 100 ms 60 ms statique C++03 mutexée OUI 6.3 s 11.5 s DCLP thread-unsafe NON 410 ms 50 ms DCLP avec atomic TBB OUI 400 ms 240 ms DCLP avec atomic C++11 OUI 400 ms 70 ms Magic static C++11 OUI N/A 70 ms
  46. 46. Qu'est-ce que ça change pour le développement multi-cible? Environnement Windows Environnement Linux Statiques locales Thread-unsafe avec VS 2013 Thread-safe avec gcc Magic statics Thread-safe avec VS 2015 et gcc ➢ Etre iso sur toutes les cibles, c'est mieux ☺
  47. 47. DCLP sert encore à quelque chose? ➢ Le singleton est un cas particulier de l'idiome d'initialisation tardive (lazy) ➢ Les objets doivent être connus à l'avance (un objet ⇔ une magic static) ➢ DCLP reste utile si le nombre d'objets est variable (cache dynamique) Calcul et caching de la somme des données L'accumulateur est construit à partir d'un vecteur de données DCLP pour implémenter un calcul lazy de la somme des données
  48. 48. DCLP dans le langage avec std::call_once ➢std::call_once encapsule l'appel à une fonction ➢ La fonction ne sera appelée qu'une seule fois au maximum
  49. 49. Conclusions
  50. 50. Qu'est-ce qui change en C++11? • Initialisation dynamique des statiques non-locales – Pas de changement majeur en monothread (SIOF!) – En multithread, l'initialisation peut être concurrente • Initialisation des statiques locales et singleton – C++11 intègre les magic statics – L'initialisation du singleton est toujours thread-safe – DCLP est inutile pour le singleton • DCLP – DCLP est réparé avec les atomiques et le memory-model – A connaître pour le caching dynamique – Peut-être réalisé avec std::call_once
  51. 51. Disclaimer • Vous n'avez rien appris de très utile... ☺ • Mais quand même... – Pensez au static initialization order fiasco – Pensez à DCLP/call_once pour les caches dynamiques – Remplacer les atomiques Boost/TBB par C++11? • Au menu pour un prochain tutorial – Grandeur et déclin du mot-clé volatile – Le memory model de C++11

×