C ++ Russia Piter 2019 rapporte l'examen

Dans un programme conjoint Master ITMO et JetBrains, nous demandons aux étudiants envoyés à la conférence de rédiger un rapport avec une revue des rapports.
Nous publions l'un de ces rapports sur la conférence C ++ Russia Piter 2019. L'auteur est un étudiant de 2e année Artyom Khoroshev.



Début novembre, j'ai assisté à la conférence cpp-russia-piter, ci-dessous je parlerai des reportages dont je me souviens.

Roman Rusyaev: exceptions C ++ à travers le prisme des optimisations du compilateur


Un rapport intéressant dans lequel le conférencier sur l'exemple de LLVM a parlé des exceptions de coût nul dans le C ++ moderne.

LLVM IR présente le programme sous forme de graphique de flux de contrôle. Dans les nœuds du graphique se trouvent des blocs d'instructions à suivre. À la fin de chaque bloc, il y a un terminateur qui transfère le contrôle au bloc suivant. Le terminateur peut être une transition conditionnelle vers un autre bloc, une instruction de retour ou une instruction d'invocation spéciale, qui a la sémantique d'appeler la fonction et, en cas de succès, transfère le flux de contrôle à un bloc et, en cas d'exception, indique le bloc auquel vous souhaitez le traiter. Les instructions dans un bloc ont la propriété que si nous frappons un bloc, nous exécuterons toutes les instructions, ou nous ne tomberons pas du tout dans ce bloc. Il existe des optimisations qui ne peuvent fonctionner que dans un seul bloc. Dans le cas de petits blocs, ils auront un contexte plus petit, par conséquent, il est pire de faire des optimisations.
L'orateur a expliqué comment les compilateurs modernes peuvent convertir des instructions d'invocation en instructions d'appel, qui ne sont plus terminales, et, par conséquent, donner au compilateur plus de place pour les optimisations. Mais, pour ne pas compter sur le compilateur, vous pouvez écrire la fonction noexcept vous-même (si cela est correct) pour être sûr que le compilateur effectuera toutes les optimisations.

(diapositives du rapport)

Maxim Khizhinsky: un logement de classe confort pour les acteurs et les maîtres


L'orateur s'est fixé pour objectif de se débarrasser d'un certain nombre de problèmes liés à la programmation parallèle:

  • données partagées
  • changement de contexte,
  • synchronisation
  • Création fréquente de flux à la volée pour des besoins à court terme.

En conséquence, l'orateur a suggéré de diviser son programme en composants, dont la durée de vie sera égale à la durée de vie du programme, et de mettre les composants en "appartements", dont le nombre devrait être égal au nombre de threads. En conséquence, les composants eux-mêmes sont monothread et la communication entre les composants doit se produire via le passage de messages. Je suis d'accord que cela résout le problème, mais au prix de toute la personnalisation de notre programme, nous devons le faire au moment de la compilation. Au minimum, il est nécessaire au moment de la compilation de répartir uniformément les composants entre les «appartements», ce qui empêche en outre le système de se rééquilibrer en fonction de la charge. Par conséquent, à mon avis, la solution elle-même ne semble pas assez flexible.

(diapositives du rapport)

Nikolay Beloborodov: L'utilisation d'allocateurs de dalles dans des applications réseau très chargées


Le nom parle de lui-même. L'orateur a expliqué comment ils avaient considérablement amélioré les performances du système à l'aide d'un répartiteur de dalles . L'allocateur de dalles opère sur plusieurs entités:

  • la dalle est un morceau de mémoire contigu (généralement une taille fixe) qui est divisé en sections de la même taille. Ces zones sont utilisées pour stocker des objets de même taille,
  • cache - une liste de dalles avec la même division,
  • allocateur de dalles - un ensemble de caches.

Grâce à cette construction, des objets de même taille sont stockés localement. La désallocation est conçue comme une marque qu'un site particulier est faible et peut être réutilisé. Cela évite la fragmentation de la mémoire.

A partir d'une telle définition de la dalle d'un allocateur, il devient clair qu'elle est bien adaptée pour mettre en évidence la libération d'objets dont la taille se situe dans une plage limitée. Par exemple, en allouant une taille de plus en plus grande à chaque fois, un nouveau cache sera créé, les anciens caches ne seront pas réutilisés.

L'orateur a déclaré que, pour cette raison, ils devaient abandonner certains conteneurs au profit d'autres. Par exemple, le vecteur a été remplacé par une liste, hashmap avec un arbre, mais néanmoins, un gain de performances a quand même été obtenu.

(diapositives du rapport)

Anton Polukhin: astuces de taxi C ++


Les rapports d'Anton Polukhin sont toujours intéressants et les solutions qu'il propose semblent bonnes. Cette fois, Anton a montré comment le modèle de bouton peut être amélioré en termes d'allocation dynamique. Pour ce faire, vous devez placer le stockage de l'objet d'implémentation dans l'objet lui-même. Permettez-moi de vous rappeler que le motif de bouton classique est le suivant:

// Foo.h struct Foo { Foo(); private: struct Foo_impl; //forward declaration std::unique_ptr<Foo_impl> impl; }; // Foo.cpp //implementation struct Foo::Foo_impl { }; 

Nous voulons nous débarrasser de l'allocation dynamique, pour cela nous préparerons une place à l'avance directement dans l'objet Foo:
 // Foo.h struct Foo { Foo(); private: struct Foo_impl; //forward declaration std::aligned_storage_t<sizeof(Foo_impl), alignof(Foo_impl)> impl; }; // Foo.cpp //implementation struct Foo::Foo_impl { } 

Cette méthode ne fonctionnera pas, car nous n'avons pas d'informations complètes sur le type Foo_impl dans Foo.h et une erreur de compilation sera reçue. La seule solution qui reste est de deviner à l'avance la taille du stockage.

 // Foo.h struct Foo { Foo(); private: struct Foo_impl; //forward declaration constexpr std::size_t kImplSize = 32; constexpr std::size_t kImplAlign = 8; std::aligned_storage_t<kImplSize, kImplAlign> impl; }; // Foo.cpp //implementation struct Foo::Foo_impl { } 

Mais vous devez ajouter une vérification que la taille est toujours correcte. Cela est nécessaire, car une tentative de placer l'objet dans un tampon inapproprié pour lui est UB.

 // Foo.h struct Foo { Foo(); ~Foo(); private: constexpr std::size_t kImplSize = 32; constexpr std::size_t kImplAlign = 8; struct Foo_impl; //forward declaration std::aligned_storage_t<kImplSize, kImplAlign> impl; }; // Foo.cpp //implementation struct Foo::Foo_impl { } struct Foo::~Foo() { static_assert(kImplSize==sizeof(Foo_impl),"Size and sizeof(T) mismatch"); static_assert(kImplAlign==alignof(kImplAlign),"Alignment and alignof(T) mismatch"); // call destructor of Foo_impl } 

Nous effectuons une vérification dans le fichier cpp, et si quelque chose n'est pas indiqué correctement, nous terminons par une erreur de compilation et imprimons la taille correcte de la structure afin que le programmeur puisse deviner dès la deuxième tentative.

Anton a montré comment rendre sa bibliothèque de sérialisation dans différents formats pratique, sans oublier la fonctionnalité ADL: s'il existe des paramètres de modèle pour les arguments de fonction, la fonction sera recherchée dans l'espace de nom de paramètre de ces arguments.

(diapositives du rapport)

Eric Niebler: une abstraction unificatrice pour l'async en C ++


Un rapport intéressant, qui discute des problèmes des abstractions asynchrones dans la norme de langage existante: pourquoi l'avenir et la promesse sont lents, et pouvons-nous concevoir la bibliothèque de manière à éviter ces frais généraux. Les développeurs de Facebook semblent avoir une solution décente https://github.com/facebookexperimental/libunifex

(diapositives du rapport)

Dmitry Kozhevnikov et Andrey Davydov: deux rapports sur les modules


Le programme a eu deux rapports consécutifs sur les modules. Après avoir écouté les deux rapports, il est devenu clair que les modules ne sont pas encore prêts à l'emploi. Cela m'a un peu bouleversé, car je ne m'intéressais pas en principe à la façon dont cette nouvelle fonctionnalité du langage est implémentée, et j'ai pensé que C ++ 20 sortirait et serait prêt à être utilisé immédiatement. Malheureusement, il s'est avéré que ce n'est pas le cas.

(diapositives des rapports: 1 , 2 )

Conclusion


La dernière conférence s'est réjouie d'exemples intéressants d'utilisation de fonctionnalités bien connues du langage. Un grand nombre de rapports concernaient les puces de la norme suivante - C ++ 20. Ceci, bien sûr, est très utile à tous les développeurs C ++.

Source: https://habr.com/ru/post/fr480798/


All Articles