Pourquoi les types entiers rapides sont-ils plus rapides que les autres types entiers?

107

Dans l'ISO / CEI 9899: 2018 (C18), il est indiqué sous 7.20.1.3:

7.20.1.3 Types entiers de largeur minimale les plus rapides

1 Chacun des types suivants désigne un type entier qui est généralement le plus rapide 268) pour fonctionner avec tous les types entiers qui ont au moins la largeur spécifiée.

2 Le nom typedef int_fastN_tdésigne le type entier signé le plus rapide avec une largeur d'au moins N. Le nom typedef uint_fastN_tdésigne le type entier non signé le plus rapide avec une largeur d'au moins N.

3 Les types suivants sont requis:

int_fast8_t, int_fast16_t, int_fast32_t, int_fast64_t, uint_fast8_t, uint_fast16_t, uint_fast32_t,uint_fast64_t

Tous les autres types de ce formulaire sont facultatifs.


268) Le type désigné n'est pas garanti d'être le plus rapide à toutes fins; si la mise en œuvre n'a pas de raisons claires de choisir un type plutôt qu'un autre, elle choisira simplement un type entier satisfaisant aux exigences de signature et de largeur.


Mais il n'est pas précisé pourquoi ces types entiers "rapides" sont plus rapides.

  • Pourquoi ces types entiers rapides sont-ils plus rapides que les autres types entiers?

J'ai marqué la question avec C ++, car les types d'entiers rapides sont également disponibles en C ++ 17 dans le fichier d'en-tête de cstdint. Malheureusement, dans l'ISO / CEI 14882: 2017 (C ++ 17), aucune explication de ce type n'existe; J'avais mis en œuvre cette section autrement dans le corps de la question.


Information: en C, ils sont déclarés dans le fichier d'en-tête de stdint.h.

RobertS soutient Monica Cellio
la source
24
Le point clé ici est que ces types entiers ne sont pas des types séparés, magiquement plus rapides. Ce ne sont que des alias de tout type existant normal qui est le plus rapide sur cette machine pour cette opération.
mtraceur
3
Le compilateur émet des opcodes d'opération CPU pour charger, stocker, masquer et modifier les emplacements de mémoire et les registres de tailles spécifiques; c'est tout ce que le CPU voit. Le système d'exploitation n'a rien à voir avec cela. C'est tout ce que fait le compilateur, exactement comme si vous aviez spécifié vous-même le typedef donné. (Je suppose qu'un compilateur est autorisé à le traiter en interne différemment - peut-être plus efficacement, si cela est possible - qu'un utilisateur typedef, tant qu'il n'y a pas de différence de comportement visible.)
Peter - Rétablir Monica
1
@ RobertS-ReinstateMonica Pour être précis, ces "alias" ne sont que des typedefdéclarations. Donc, généralement , cela se fait au niveau de la bibliothèque standard. Bien sûr, les C standards ne met aucune restriction réelle sur ce qu'ils typedefà - ainsi , par exemple une mise en œuvre typique est de faire int_fast32_tun typedefde intsur un système 32 bits, mais un compilateur hypothétique pourrait par exemple mettre en œuvre un __int_fasttype intrinsèque et promettent de faire un peu de fantaisie optimisations pour choisir le type de machine le plus rapide au cas par cas pour les variables de ce type, et la bibliothèque pourrait alors le typedeffaire.
mtraceur
1
@ RobertS-ReinstateMonica Oui, c'est vrai. Vous obtenez des programmes de performances maximales avec des drapeaux de compilation spécifiques à l'architecture, ce qui rend le binaire moins portable.
Peter - Réintègre Monica le
1
@ RobertS-ReinstateMonica Il sera plus efficace sur la plate - forme , il a été compilé pour , pas nécessairement sur .
HABO

Réponses:

152

Imaginez un processeur qui effectue uniquement des opérations arithmétiques 64 bits. Imaginez maintenant comment vous implémenteriez un ajout 8 bits non signé sur un tel processeur. Il faudrait nécessairement plus d'une opération pour obtenir le bon résultat. Sur un tel processeur, les opérations 64 bits sont plus rapides que les opérations sur d'autres largeurs entières. Dans cette situation, tous Xint_fastY_tpourraient vraisemblablement être un alias de type 64 bits.

Si un processeur prend en charge les opérations rapides pour les types entiers étroits et qu'un type plus large n'est donc pas plus rapide qu'un type plus étroit, il Xint_fastY_tne s'agira pas d'un alias du type plus large que nécessaire pour représenter tous les Y bits.

Par curiosité, j'ai vérifié les tailles d'une implémentation particulière (GNU, Linux) sur certaines architectures. Ce ne sont pas les mêmes pour toutes les implémentations sur la même architecture:

┌────╥───────────────────────────────────────────────────────────┐
 Y     sizeof(Xint_fastY_t) * CHAR_BIT                         
    ╟────────┬─────┬───────┬─────┬────────┬──────┬────────┬─────┤
     x86-64  x86  ARM64  ARM  MIPS64  MIPS  MSP430  AVR 
╞════╬════════╪═════╪═══════╪═════╪════════╪══════╪════════╪═════╡
 8   8       8    8      32   8       8     16      8   
 16  64      32   64     32   64      32    16      16  
 32  64      32   64     32   64      32    32      32  
 64  64      64   64     64   64      64    64      64  
└────╨────────┴─────┴───────┴─────┴────────┴──────┴────────┴─────┘

Notez que bien que les opérations sur les types plus grands puissent être plus rapides, ces types prennent également plus d'espace dans le cache, et donc leur utilisation ne donne pas nécessairement de meilleures performances. En outre, on ne peut pas toujours croire que la mise en œuvre a fait le bon choix en premier lieu. Comme toujours, la mesure est requise pour des résultats optimaux.


Capture d'écran du tableau, pour les utilisateurs d'Android:

Capture d'écran du tableau ci-dessus

(Android n'a pas de caractères de dessin de boîte dans la police mono - ref )

eerorika
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Samuel Liew
@RobertSsupportsMonicaCellio Non. "Pas la même dans toutes les architectures" est également vrai, mais est immédiatement évident à partir des données affichées, donc je ne considérerais pas nécessaire de dire l'évidence. Je n'ai montré que les valeurs d'une implémentation, et d'autres auront en effet des choix différents. Vérifiez par exemple x86-64 sur Windows. Vous trouverez différentes tailles par rapport à ce qui a été montré ici.
eerorika
@RobertSsupportsMonicaCellio À mon avis, ces commentaires sont pertinents pour la réponse et appropriés ici. Je laisserais un modérateur les déplacer s'ils en ressentent le besoin.
eerorika
11

Ils ne le sont pas, du moins pas de manière fiable.

Les types rapides sont simplement des typedefs pour les types réguliers, mais c'est à l'implémentation comment les définir. Ils doivent être au moins de la taille demandée, mais ils peuvent être plus grands.

Il est vrai que sur certaines architectures certains types entiers ont de meilleures performances que d'autres. Par exemple, les premières implémentations ARM avaient des instructions d'accès à la mémoire pour les mots 32 bits et pour les octets non signés, mais elles n'avaient pas d'instructions pour les demi-mots ou les octets signés. Les instructions d'un demi-mot et d'octets signés ont été ajoutées plus tard, mais elles ont toujours des options d'adressage moins flexibles, car elles devaient être placées dans l'espace de codage de rechange. De plus, toutes les instructions de traitement des données réelles sur ARM fonctionnent sur les mots, donc dans certains cas, il peut être nécessaire de masquer les valeurs plus petites après le calcul pour donner des résultats corrects.

Cependant, il y a aussi la préoccupation concurrente de la pression du cache, même s'il faut plus d'instructions pour charger / stocker / traiter une valeur plus petite. La valeur plus petite peut tout de même fonctionner mieux si elle réduit le nombre de ratés de cache.

Les définitions des types sur de nombreuses plates-formes communes ne semblent pas avoir été réfléchies. En particulier, les plates-formes 64 bits modernes tendent à avoir une bonne prise en charge des entiers 32 bits, mais les types "rapides" sont souvent inutilement 64 bits sur ces plates-formes.

De plus, les types en C font partie de l'ABI de la plateforme. Ainsi, même si un fournisseur de plate-forme découvre qu'il a fait des choix stupides, il est difficile de modifier ces choix stupides plus tard.

Ignorez les types "rapides". Si vous êtes vraiment préoccupé par les performances entières, comparez votre code avec toutes les tailles disponibles.

plugwash
la source
7

Les types rapides ne sont pas plus rapides que tous les autres types entiers - ils sont en fait identiques à certains types entiers "normaux" (ils ne sont qu'un alias pour ce type) - quel que soit le type qui s'avère le plus rapide pour conserver une valeur de au moins autant de bits.

Il dépend simplement de la plate-forme pour quel type entier chaque type rapide est un alias.

Chris Dodd
la source