J'ai beaucoup étudié les différents entiers et leur rapprt usage / performance, mon travail consistant à faire de la formation et du conseil en calcul HPC.
Entre les signés et les non signés, pour moi le choix est clair : il faut utiliser les entiers signés, sauf si on veut absolument que l'entier se comporte modulo 2n, ou si malheureusement on y est contraint par la STL. Je sais que je peux m'attirer les foudres de nombreux programmeurs C++, mais Stroustrup, Sutter et Carruth sont avec moi, donc je me sens moins seul :-). Les entiers non signés devraient d'ailleurs être appelés entiers "modulo". Les entiers signés sont beaucoup moins dangereux que les non signés. Je donne un gag sur ma page, mais un autre est le suivant :
Le jour où point ne contient aucun, élément, c'est la catastrophe.
Au niveau performance, c'est 50 / 50 :
- La division est un peu plus rapide pour les entiers non signés. Pour les autres opérations c'est exactement pareil car ce sont les mêmes opérations au niveau assembleur.
- De nombreuses optimisations peuvent être faites sur les entiers signés comme : i < i + 1 est toujours vrai, a[i] et a[i + 1] sont cote à cote dans la mémoire, 10* i / 5 = 2 * i. Vous verrez que tout cela est faux pour les types non signés. Je vois souvent des compilateurs refuser certaines optimisations comme la vectorisation car les types non signés leur font peur. Une analyse fine montre souvent que cette vectorisation était quand même posssible, mais comme certaines autres ne le sont pas, la vue de ces entiers doit faire fuir les optimseurs :-)
Donc, mon conseil est de toujours traiter avec des types signés et de faire un static_cast vers un unsigned (ça ne génère aucun code assembleur) si on doit faire de nombreuses divisions dans une partie critique d'un code avec des entiers signés dont on sait qu'ils sont positifs. Au passage, cela ne m'est jamais arrivé de le faire :-)
Concernant la performance des entiers 32 bits ou 64 bits, pour indexer les boucles (je ne veux pas entendre parler d'itérateurs pour les tableaux, car les indices sont beaucoup plus pratique : on peut faire leur moyenne, ils ne sont pas invalidés quand on fait un resize, il n'existe pas d'iterateur efficace pour les tableaux multidimensionnels dès qu'on fait du padding, etc), il faut savoir que pour calculer p[i] lorsque p est un pointeur et i un entier, il faut une instruction d'assembleur pour faire passer i du 32 bit au 64 bit avant de l'ajouter à p. Donc les entiers 64-bit seraient en pratique un peu plus efficace que les entiers 32 bits pour indexer. Maintenant, les compilateurs font tellement de transformation sur une boucle (unrolling, vectorisation) que cette instruction n'a en pratique aucun effet notable. Bref, le match est 50-50 pour moi.
Par contre lorsqu'un traite des tableaux d'indices (on a beaucoup d'entiers), la il faut prendre le type le plus petit possible (donc 32-bit) pour que la vectorisation soit le plus efficace possible.
Sur d'autres algorithmes, cela dépend. Si par exemple, on cherche le i tel que v[i] est minimal dans un std::vector, il vaut mieux utiliser un entier 64-bit car les doubles sont sur 64-bits. Si on remplace le double par un float, il vaut mieux un indice codé sur 32-bit, comme le float. Cela permet au compilateur de gérer les lignes vectorielles plus efficacement.
En gros, dans un monde sans STL, mon conseil pour la performance et les indices des boucles est le suivant : utilisez un entier signé. Quand à savoir si il faut le prendre en 32 bit ou 64-bit, faites ce que vous voulez, mais sachez qu'il est parfois nécessaire de choisir le bon pour la performance ultime.
[^] # Re: Value Semantics Rocks
Posté par magnolia . En réponse à la dépêche C++17 garantit le court-circuit de copie (suite de la précédente dépêche). Évalué à 4. Dernière modification le 14 décembre 2016 à 23:32.
J'ai beaucoup étudié les différents entiers et leur rapprt usage / performance, mon travail consistant à faire de la formation et du conseil en calcul HPC.
Entre les signés et les non signés, pour moi le choix est clair : il faut utiliser les entiers signés, sauf si on veut absolument que l'entier se comporte modulo 2n, ou si malheureusement on y est contraint par la STL. Je sais que je peux m'attirer les foudres de nombreux programmeurs C++, mais Stroustrup, Sutter et Carruth sont avec moi, donc je me sens moins seul :-). Les entiers non signés devraient d'ailleurs être appelés entiers "modulo". Les entiers signés sont beaucoup moins dangereux que les non signés. Je donne un gag sur ma page, mais un autre est le suivant :
Le jour où point ne contient aucun, élément, c'est la catastrophe.
Au niveau performance, c'est 50 / 50 :
- La division est un peu plus rapide pour les entiers non signés. Pour les autres opérations c'est exactement pareil car ce sont les mêmes opérations au niveau assembleur.
- De nombreuses optimisations peuvent être faites sur les entiers signés comme : i < i + 1 est toujours vrai, a[i] et a[i + 1] sont cote à cote dans la mémoire, 10* i / 5 = 2 * i. Vous verrez que tout cela est faux pour les types non signés. Je vois souvent des compilateurs refuser certaines optimisations comme la vectorisation car les types non signés leur font peur. Une analyse fine montre souvent que cette vectorisation était quand même posssible, mais comme certaines autres ne le sont pas, la vue de ces entiers doit faire fuir les optimseurs :-)
Donc, mon conseil est de toujours traiter avec des types signés et de faire un static_cast vers un unsigned (ça ne génère aucun code assembleur) si on doit faire de nombreuses divisions dans une partie critique d'un code avec des entiers signés dont on sait qu'ils sont positifs. Au passage, cela ne m'est jamais arrivé de le faire :-)
Concernant la performance des entiers 32 bits ou 64 bits, pour indexer les boucles (je ne veux pas entendre parler d'itérateurs pour les tableaux, car les indices sont beaucoup plus pratique : on peut faire leur moyenne, ils ne sont pas invalidés quand on fait un resize, il n'existe pas d'iterateur efficace pour les tableaux multidimensionnels dès qu'on fait du padding, etc), il faut savoir que pour calculer p[i] lorsque p est un pointeur et i un entier, il faut une instruction d'assembleur pour faire passer i du 32 bit au 64 bit avant de l'ajouter à p. Donc les entiers 64-bit seraient en pratique un peu plus efficace que les entiers 32 bits pour indexer. Maintenant, les compilateurs font tellement de transformation sur une boucle (unrolling, vectorisation) que cette instruction n'a en pratique aucun effet notable. Bref, le match est 50-50 pour moi.
Par contre lorsqu'un traite des tableaux d'indices (on a beaucoup d'entiers), la il faut prendre le type le plus petit possible (donc 32-bit) pour que la vectorisation soit le plus efficace possible.
Sur d'autres algorithmes, cela dépend. Si par exemple, on cherche le i tel que v[i] est minimal dans un std::vector, il vaut mieux utiliser un entier 64-bit car les doubles sont sur 64-bits. Si on remplace le double par un float, il vaut mieux un indice codé sur 32-bit, comme le float. Cela permet au compilateur de gérer les lignes vectorielles plus efficacement.
En gros, dans un monde sans STL, mon conseil pour la performance et les indices des boucles est le suivant : utilisez un entier signé. Quand à savoir si il faut le prendre en 32 bit ou 64-bit, faites ce que vous voulez, mais sachez qu'il est parfois nécessaire de choisir le bon pour la performance ultime.