Actuellement, je travaille avec une base de code (C, C++ mixte) ciblée pour une plateforme MIPS 32 bits. Le processeur est assez moderne [pour mentionner que nous avons une bonne quantité de puissance de traitement et de mémoire].
La base de code utilise des types de données comme uint8 [entier non signé large de 1 octet], uint16 [entier non signé large de 2 octets], uint32 [entier non signé large de 4 octets] etc.
Je sais à quel point l'utilisation de ces constructions est utile lors du portage du code sur différentes plates-formes.
Mes questions sont:
À quoi sert/avantage d'utiliser un uint16 où un uint32 suffira également (s'il y en a)?
Y aura-t-il des économies dans l'utilisation de la mémoire en utilisant des types de données plus courts (compte tenu de l'alignement des données)?
Si c'est pour économiser quelques octets de mémoire, est-ce quelque chose de sensé à faire dans du matériel moderne?
À quoi sert/avantage d'utiliser un uint16 où un uint32 suffira également (s'il y en a)?
Si ces uint16s
font partie de tableaux ou de structures, vous pouvez économiser de la mémoire et peut-être être en mesure de gérer des ensembles de données plus volumineux qu'avec uint32s
dans ces mêmes tableaux ou structures. Cela dépend vraiment de votre code.
Les protocoles de données et les formats de fichiers peuvent utiliser uint16s
et il peut ne pas être correct d'utiliser uint32s
au lieu. Cela dépend du format et de la sémantique (par exemple, si vous avez besoin de valeurs pour passer de 65535 à 0, uint16
le fera automatiquement pendant que uint32
non).
OTOH, si ces uint16s
ne sont que des variables locales ou globales uniques, les remplacer par des variables 32 bits peut ne pas faire de différence significative car elles sont susceptibles d'occuper le même espace en raison de l'alignement et elles sont transmises en tant que paramètres 32 bits (sur la pile ou dans les registres ) sur MIPS de toute façon.
Y aura-t-il des économies dans l'utilisation de la mémoire en utilisant des types de données plus courts (compte tenu de l'alignement des données)?
Il peut y avoir des économies, surtout lorsque uint16s
font partie de nombreuses structures ou éléments de grands tableaux.
Si c'est pour économiser quelques octets de mémoire, est-ce quelque chose de sensé à faire dans du matériel moderne?
Oui, vous réduisez la bande passante mémoire (ce qui est toujours une bonne chose) et vous réduisez souvent divers échecs de cache (caches de données et TLB) lorsque vous utilisez moins de données.
Tout d'abord, si vous avez défini des types tels que uint16, où sont-ils définis? Ce ne sont pas des types standard, ils seront donc définis dans un en-tête propriétaire - peut-être le vôtre ou peut être fourni par une bibliothèque tierce; dans ce cas, vous devez vous demander à quel point ce code est portable et si vous créez une dépendance qui pourrait ne pas avoir de sens dans une autre application.
Un autre problème est que de nombreuses bibliothèques (malencontreusement IMO) définissent de tels types avec divers noms tels que UINT16, uint16, U16 UI16, etc. Si de tels noms sont définis, ils devraient idéalement être placés dans un espace de noms ou avoir un préfixe spécifique à la bibliothèque pour indiquer avec quelle bibliothèque ils ont été définis pour être utilisés, par exemple rtos::uint16
à rtos_uint16
.
Étant donné que la bibliothèque de normes ISO C99 fournit des types spécifiques de longueur de bits standard dans stdint.h, vous devriez préférer leur utilisation à celles définies dans un en-tête propriétaire ou tiers. Ces types ont un _t
suffixe, par exemple uint16_t
. En C++, ils peuvent être placés dans le std::
namespace (bien que ce ne soit pas une donnée puisque l'en-tête a été introduit en C99).
1] À quoi sert/avantage d'utiliser un uint16 où un uint32 suffira également (s'il y en a)?
Mis à part mes conseils précédents, préférez stdint.h
's uint16_t
, il existe au moins deux raisons légitimes d'utiliser des types spécifiques à la longueur:
2] Y aura-t-il des économies dans l'utilisation de la mémoire en utilisant des types de données plus courts (compte tenu de l'alignement des données)?
Peut-être, mais si la mémoire n'est pas votre problème, ce n'est pas une bonne raison de les utiliser. Il vaut peut-être la peine d'envisager des objets ou des tableaux de données volumineux, mais l'application à l'échelle mondiale en vaut rarement la peine.
3] Si c'est pour économiser quelques octets de mémoire, est-ce quelque chose de sensé à faire dans du matériel moderne?
Voir [2]. "matériel moderne" n'implique cependant pas nécessairement de grandes ressources; il y a beaucoup de 32 bits ARM Cortex-M avec seulement quelques Kb of RAM par exemple. C'est-à-dire plus sur l'espace, le coût et la consommation d'énergie que sur l'âge de la conception ou de l'architecture.
cstdint
contient des charges de typedef
s à des fins différentes.
intN_t
pour une largeur spécifiqueint_fastN_t
pour l'entier le plus rapide, qui a au moins N bitsint_leastN_t
pour le plus petit entier, qui a au moins N bitsunsigned
équivalentsVous devez choisir en fonction de votre situation. Stocker des milliers dans un std::vector
et ne fait pas beaucoup de calculs? intN_t
est probablement votre homme. Besoin d'un calcul rapide sur un petit nombre d'entiers? int_fastN_t
est probablement votre gars.
Il faut vérifier le code machine/assembleur produit pour vérifier qu'il y a une sauvegarde de code. Dans les architectures de type RISC, l'immédiat typique est de 16 bits, mais l'utilisation de uint16_t consommera de toute façon un registre complet de 32 bits - donc, si vous utilisez des types int, mais s'engager à utiliser des valeurs proches de zéro produira les mêmes résultats et sera plus portable.
La sauvegarde de la mémoire de l'OMI vaut également sur les plates-formes modernes. Un code plus strict conduit par exemple à meilleure autonomie de la batterie et UX plus fluide. Cependant, je suggérerais de micro-gérer la taille uniquement lorsque vous travaillez avec des (grandes) baies, ou lorsque la variable correspond à une ressource matérielle réelle.
ps. Les compilateurs sont intelligents, mais les gens qui les écrivent travaillent pour le moment, ce qui les rend encore meilleurs.
Rép. 1. Le logiciel a certaines exigences et spécifications qui indiquent strictement de ne prendre que 8/16 bits d'un paramètre lors du codage/décodage ou d'une autre utilisation certaine. Donc, même si u attribue une valeur supérieure à 127 dans un disons u8, il coupe automatiquement les données pour vous.
Rép. 2. Nous ne devons pas oublier que nos compilateurs sont bien au-delà de l'intelligence pour faire l'optimisation, que ce soit la mémoire ou la complexité. Il est donc toujours recommandé d'utiliser une mémoire plus petite lorsque cela est possible.
Rép. 3. Bien sûr, économiser de la mémoire est logique sur les appareils modernes.
Utilisation de types entiers de largeur exacte comme int32_t
et amis est utile pour éviter les bogues d'extension de signe entre les plates-formes qui ont des tailles différentes pour int
et long
. Cela peut se produire lors de l'application de masques de bits ou lors du décalage de bits, par exemple. Si vous effectuez ces opérations sur un long
par exemple, et que votre code fonctionne pour un long
32 bits, il peut se casser pour un long
64 bits. Si par contre vous utilisez un uint32_t
, vous savez exactement quels résultats vous obtiendrez quelle que soit la plateforme.
Ils sont également utiles pour échanger des données binaires entre plates-formes, où vous n'avez qu'à vous soucier de l'endianité des données stockées et non de la largeur de bit; si vous écrivez un int64_t
dans un fichier, vous savez que sur une autre plate-forme, vous pouvez le lire et le stocker dans un int64_t
. Si vous écriviez un long
au lieu de 64 bits sur une seule plate-forme, vous pourriez avoir besoin d'un long long
sur une autre plate-forme car long
il n'y a que 32 bits.
Sauver la mémoire n'est généralement pas la raison, sauf si vous parlez d'environnements très limités (trucs intégrés) ou de grands ensembles de données (comme un tableau avec 50 millions d'éléments ou autres).
En utilisant uint16_t
au lieu de uint32_t
économise de la mémoire. Cela peut également être une contrainte matérielle (par exemple, un contrôleur périphérique envoie vraiment 16 bits!) Cependant, cela peut ne pas valoir la peine de l'utiliser, en raison de considérations de cache et d'alignement (vous devez vraiment comparer).
À quoi sert/avantage d'utiliser un uint16 où un uint32 suffira également (s'il y en a)?
Il existe des CPU où unsigned char
est une valeur de 16 bits. Le test unitaire de ce code serait difficile sans l'utilisation de typedefs (uint16 n'est qu'un typedef pour le type approprié).
De plus, avec l'utilisation de ces typedefs, il est plus facile de construire sur différentes plates-formes sans beaucoup de problèmes.
Y aura-t-il des économies dans l'utilisation de la mémoire en utilisant des types de données plus courts (compte tenu de l'alignement des données)?
Non, ce n'est pas un point. Si uint16
est un typedef pour unsigned short
, vous pouvez alors utiliser unsigned short
partout, mais vous pouvez obtenir différents types sur différentes plates-formes.
Bien sûr, l'utilisation d'un type plus petit réduira la consommation de mémoire. Par exemple, en utilisant uint16 au lieu de uint32, mais uniquement si vous utilisez des tableaux.
Si c'est pour économiser quelques octets de mémoire, est-ce quelque chose de sensé à faire dans du matériel moderne?
Cela dépend de la plateforme:
Les réponses à vos questions se résument à un concept clé: quelle est la taille des données? Si vous en consommez beaucoup, l'avantage d'utiliser des types de données plus petits est évident. Pensez-y de cette façon: le simple calcul du plus grand nombre premier connu récemment découvert pourrait vous faire manquer de mémoire sur un poste de travail typique. Le nombre lui-même prend plus d'un gigaoctet juste pour le stocker. Cela n'inclut pas le calcul du nombre réel. Si vous deviez utiliser un type de données épais au lieu d'un type mince, vous envisagez peut-être deux gigaoctets à la place. Un exemple simpliste, mais néanmoins bon.