IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Apprendre à programmer avec C++14 et C++17 pour des codes plus rapides et performants
Un tutoriel de Dan Levin, traduit par Sabine Chiesa

Le , par Community Management

0PARTAGES

7  0 
Chers membres du club,

J'ai le plaisir de vous présenter ce tutoriel de Dan Levin pour vous apprendre à rendre vos codes plus rapides (performants) avec C++14 et C++17.

L'écriture de codes performants est toujours une tâche difficile. L'application directe des algorithmes théoriques « purs » n'est pas toujours suffisante dans les architectures du monde réel.

Lorsqu'on a commencé à améliorer la rapidité de ces algorithmes purs, on se trouve rapidement confronté à un dilemme : certaines implémentations s'avèrent relativement rapides sur une architecture, mais effroyablement lentes sur d'autres. Dans le même temps, dans certains contextes, une nouvelle implémentation va dépasser les performances de la première, mais perdre de la vitesse dans tous les autres.

De nombreuses optimisations, grandes et petites, pour chacune des architectures prises en charge peuvent rapidement faire gonfler notre code et nous faire perdre du temps. Souvent, nous sommes donc ramenés à choisir entre deux options : un beau code trop lent, ou bien un code rapide mais illisible.
Bonne lecture .

Retrouvez les meilleurs cours et tutoriels pour apprendre la programmation C++

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de Luc Hermitte
Expert éminent sénior https://www.developpez.com
Le 30/09/2016 à 11:56
log2n est une variable dynamique. Tu ne peux pas l'utiliser directement comme paramètre template sans passer par un dispatching à coups de switch ou autre.
5  0 
Avatar de koala01
Expert éminent sénior https://www.developpez.com
Le 12/10/2016 à 14:45
Citation Envoyé par Oscar.STEFANINI Voir le message
Merci pour les explications, même si en fait les pointeurs j'en fait pendant 1 année non stop (mon école m'a fait faire du C pendant 1 an). J'ai pas encore eu le temps de regarder les pointeurs intelligents, mais c'est vrai que j'aime bien les références (d'ailleurs j'ai une question, est-ce que passer une référence à une fonction c'est aussi léger que passer un pointeur ?).
Oui, tout à fait... En fait, si tu regarde au niveau du code binaire généré (ou au niveau du code assembleur généré), tu te rendra compte qu'une référence est transmise exactement comme s'il s'agissait d'un pointeur: c'est l'adresse mémoire à laquelle se trouve la donnée qui est transmise

La seule différence entre les deux, c'est que le langage (et donc le compilateur) est beaucoup plus permissif en ce qui concerne les pointeurs : on peut changer la valeur de l'adresse mémoire qu'ils reprsentent, et il peuvent représenter une adresse invalide (nullptr), alors que les références doivent être définies une bonne fois pour toute à la création, et qu'elle doivent référencer un objet qui existe lorsqu'elles sont créées.
Citation Envoyé par foetus
  • unique_ptr<> -> new/ delete
  • weak_ptr<> -> XXX& ou const XXX& ou [XXX* ou const XXX*]
  • shared_ptr -> je ne sais trop, mais je dirais reference counting + une liste de weak_ptr<>
  • auto_ptr<> -> new/ delete mais le "avec sémantique stricte propriété de l'objet" semble dire copie/ transfert interdits

Humm...
  • unique_ptr<> (new/new[] delete/delete[](make_unique depuis C++14))
    1. unique responsable (propriétaire) du pointeur qui lui est confié
    2. copie et affectation interdites,
    3. copie par déplacement et affectation par déplacement autorisées
    4. possibilité de changer le pointeur sous jacent (la mémoire allouée au pointeur sous-jacent est alors automatiquement libérée) : reset
    5. possibilité de forcer la libération du pointeur sous jascent : reset
    6. possibilité de récupérer la responsabilité du pointeur sous jascent release
    7. possibilité de récupérer le pointeur sous jascent (sans libérer unique_ptr de la responsabilité) : opérateurs * et ->, get

  • shared_ptr<> (new/new[] delete/delete[](make_shared depuis C++11)) :
    1. plusieurs responsables / propriétaires du pointeur qui lui est confié(le dernier shared_ptr à référencer le pointeur s'occupe de la libération de la mémoire)
    2. copie et affectation autorisée
    3. copie par déplacement et affectation par déplacement autorisées
    4. possibilité de connaitre le nombre de références associées au pointeur sous-jacent : use_count
    5. possiblité d'obtenir un shared_ptr (voir plus loin)
    6. possibilité de modifier le pointeur sous jacent (reset)
    7. possibilité de forcer la libération du pointeur sous jacent (reset)
    8. possibilité de savoir si le pointeur sous jacent est toujours valide (operator bool)

  • weak_ptr<> (à partir d'un shared_ptr)
    1. transmet un pointeur partagé sans intervenir dans le comptage de référence (n'intervient pas dans le processus de libération de la mémoire)
    2. copie et affectation autorisées
    3. copie par déplacement et affectation par déplacement autorisées
    4. ne permet d'accéder au pointeur sous jacent qu'en créant un nouveau shared_ptr (lock)
    5. permet de connaire le nombre de références associées au pointeur sous jacent (use_count)
    6. permet de savoir si le pointeur sous-jacen n'est plus valide (expired)

  • auto_ptr<> (déprécié en C++11, supprimé en C++14)
    1. la sauce n'a jamais vraiment pris
    2. posait pas mal de problèmes, trop long à citer ici


tous les pointeurs intelligents fournissent les opérateurs de comparaison

A noter que C++11 date déjà de plus de cinq ans et que, depuis, C++14 est sortie (déjà depuis près de trois ans)... Cette dernière norme apporte apporte pas mal de nouveautés supplémentaires, et devrait être "la norme par défaut" utilisée, en remplacement de C++11
5  0 
Avatar de Luc Hermitte
Expert éminent sénior https://www.developpez.com
Le 14/10/2016 à 18:57
Ah. Je cuisine généralement sans rajouter de sel. Par contre les pointeurs intelligents... je m'en sers.
5  0 
Avatar de JolyLoic
Rédacteur/Modérateur https://www.developpez.com
Le 14/10/2016 à 9:47
Il y a mon article : http://loic-joly.developpez.com/tuto...mart-pointers/
Il est un peu vieux, mais je crois qu'il reste d'actualité.
Si je devais le réécrire aujourd'hui, j'insisterais probablement plus détaillé unique_ptr, et j'aurais plus précisé comment passer des arguments à une fonction.
4  0 
Avatar de foetus
Expert éminent sénior https://www.developpez.com
Le 15/10/2016 à 18:37
Citation Envoyé par wolinn Voir le message
J'utilise des pointeurs intelligents lorsque celà présente vraiment un intérêt sans inconvénients, mais dans un noyau de calcul intensif, il n'y a que des pointeurs stupides, on ne veut pas d'un niveau d'indirection supplémentaire, de code caché et tests divers qui brisent inutilement le flot d'exécution pour accéder aux objets.
Récemment il y a eu un gros débat sur ce fil de discussion : CppCon 2016 : persuader les programmeurs de C de migrer vers C++

En gros, il n'y a pas d'indirections voire dans certains cas [extrêmes] mieux optimiser parce que comme tout est template alors dans le code final, le compilateur a fait disparaître toutes les capsules RAII et fait 2-3 optimisations (parce qu'il a plus d'informations)

Le gros avantage, c'est la sécurité lors de la programmation: RAII + les capsules RAII, toutes les allocations mémoire sont gérées automatiquement
Le gros inconvénient: apprendre toutes les subtilités du C++ dit moderne.
4  0 
Avatar de koala01
Expert éminent sénior https://www.developpez.com
Le 11/10/2016 à 21:53
Salut,
Citation Envoyé par Oscar.STEFANINI Voir le message
c'est quoi le problème avec des pointeurs ?
Le problème des pointeurs, c'est qu'on les associe régulièrement avec l'allocation dynamique de mémoire (new ou new[]) et que, pour éviter les fuites mémoire, il faut penser à libérer cette mémoire à coup de delete (ou delete[]).

Mais, si allocation dynamique de mémoire il y a, encore faut il trouver "juste le bon moment" pour la libérer :
Tropt tôt, on risque de vouloir accéder à l'adresse mémoire représentée par le pointeur alors qu'elle a été libérée,
trop tard, on coure le risque d'avoir déjà perdu l'adresse mémoire représentée par le pointeur, et nous ne pourrons donc plus la libérer --> fuite mémoire.

De plus, il faut savoir que C++ est un langage à exception, et que ca complique encore le fait de décider que "c'est juste le bon moment" pour libérer la mémoire

Mais on a des outils très sympa pour éviter la plupart de ces problèmes : les pointeurs intelligents. Lorsqu'on les utilise, on peut assez facilement garantir que la mémoire allouée à un pointeur sera détruite... strictement au moment opportun.

Enfin, un pointeur peut aussi représenter une valeur particulière (nullptr) qui représente explicitement le fait que l'adresse représentée est invalide. Pour manipuler sereinement des pointeurs, la première chose à faire est... de s'assurer que l'adresse représentée est valide, et ca, ca demande du code et du temps à l'exécution.

C'est la raison pour laquelle on préfère généralement avoir recours aux références à chaque fois que faire se peut, car elles fournissent une garantie de "non nullité" (l'objet référencé existe forcément lorsque la référence est créée)
générer à la compilation ?? j'abandonne, trop fort pour moi
Pour faire simple : entre le code que tu écris et l'exécutable que tu lances, il y a deux étapes (en fait, il y en a plus, mais ces deux là sont suffisantes pour comprendre le fonctionnement):
  • la compilation proprement dite : le compilateur génère du "code binaire exécutable" (souvent appelé "code objet"
  • l'édition de liens : un outil appelé "éditeur de liens" reprend tous les "codes objets" générés par le compilateur, ainsi que celui utilisé qu'il trouve dans les bibliothèques pour créer l'exécutable final.

Généré à la compilation représente donc "tout ce qui est fait durant la compilation", pour faire la distinction avec "ce qui est fait lorsque l'application est exécutée". Et le compilateur peut faire énormément de choses qui permettront au code objet (et donc à l'exécutable final) d'être "plus performant".
3  0 
Avatar de nikko34
Membre éprouvé https://www.developpez.com
Le 12/10/2016 à 14:35
auto_ptr n'existe plus (il était mal fait et ne devait pas être utilisé dans les containers par ex.) Maintenant c'est std::unique_ptr et std::shared_ptr.
3  0 
Avatar de koala01
Expert éminent sénior https://www.developpez.com
Le 15/10/2016 à 16:53
Citation Envoyé par zobal Voir le message
On dirait du Jean-Claude Van Damme, en plus condescendant.
Tu n'as sans doute pas encore lu énormément de mes interventions, sinon tu saurais que j'aime utiliser des phrases choc pour faire réagir ceux qui me lisent, que j'essaye malgré tout toujours de ne pas trop heurter leur sensibilité (ou de leur faire des excuses sincères lorsque c'est le cas), mais que, à la réflexion, mes remarques sont généralement des plus sensées et dignes d'être suivies (même s'il m'arrive aussi de dire d'énormes conneries )
3  0 
Avatar de koala01
Expert éminent sénior https://www.developpez.com
Le 15/10/2016 à 19:40
Citation Envoyé par wolinn Voir le message
Je sais bien que les templates disparaissent à la compilation, mais le compilateur ne va quand même pas éliminer comme ça les compteurs de référence cachés, par exemple, et les tests qui vont avec.
Tu écris bien juste au dessus que l'absence de pénalités sur les pointeurs intelligents dépend d'optimisations du compilateur, et je suis d'accord avec celà. Ce n'est pas une caractéristique du langage imposée par la norme, à ma connaissance. Donc hypothèse à vérifier pour le compilateur utilisé.
Si ce n'est que le comptage de référence n'a lieu qu'avec std::shared_ptr...

Alors, oui, bien sur, la tentation est grande de décider d'utiliser des shared_ptr pour tout, afin de n'avoir pas à réfléchir à "qui est le propriétaire légal" d'une ressource. Mais c'est en définitive une très mauvaise idée.

La meilleure idée est globalement de partir sur l'utilisation "par défaut" d'un unique_ptr et de ne basculer sur un shared_ptr que si l'on a vraiment pas d'autre choix.

En adoptant cette manière de travailler, le comptage de référence et les tests que cela implique ne pose plus aucun problème, vu qu'il n'y en a pas avec les unique_ptr
[EDIT]En plus, le comptage de référence se fait de manière atomique, si bien que cela se traduit sans doute par une instruction unique ou au pire deux au niveau du code assembleur (incrémentation pour la copie / l'affectation / le lock, decrementation + saut conditionnel dans le destructeur)

Alors, tu me dira qu'il peut y avoir un problème en cas de mauvaise prédiction de la part du processeur, mais à mon avis, ca ne doit pas arriver si souvent que cela (même si je n'ai rien pour étayer ma thèse sur le sujet).
3  0 
Avatar de Luc Hermitte
Expert éminent sénior https://www.developpez.com
Le 16/10/2016 à 1:22
La gestion des pointeur nus/bruts, ça ne scale pas. Mais alors, pas du tout. Plus le nombre de chemins d'exécution augmente et plus les chances de pouvoir gérer correctement la mémoire, à la main, sans encourir de fuite s'amenuisent. Avec deux ressources (ou une ressource et des possibilités d'échec) c'est pas trivial de s'en sortir sans RAII, avec 3 ressources (ou 2 avec autres échecs) cela devient très compliqué ou lourd et inefficace, au delà, c'est l'enfer.

Ma démonstration préférée se trouve ici : http://alexandre-laurent.developpez....ou-exceptions/ On y voit successivement des codes à la C, du code C++ au pays magique où les erreurs n'existent pas, un code qui tombe en marche pour le cas particulier présenté mais qui ne supportera aucune évolution de ce code (ajout de ressources ou réorganisations), puis un code à la C qui est correct, et enfin le code C++ correct, simple et qui scale ... sans nuire aux performances.

Quant au prix, s'il est vrai que shared_ptr est loin d'être gratuit, unique_ptr (ou au pire le couple std::auto_ptr et boost:ptr_vector en C++98) ont un surcout totalement négligeable si ce n'est nul.
3  0