6
votes

Avantages / inconvénients à l'utilisation de Char pour les petits entiers en C

Y a-t-il un inconvénient d'utiliser char pour les petits entiers en C? Y a-t-il des avantages autres que l'avantage d'occupation / mémoire?

En particulier, le processeur est susceptible de faire face à une arithmétique entier sur un de chargée mieux ou pire que sur un ( long / court court >) int ?

Je sais que ce sera un processeur / système / compilateur spécifique, mais j'espère une réponse dans l'affaire Général, ou au moins, le cas général pour 32 bits Windows et Solaris, étant les systèmes que je suis travaille actuellement sur. Je suppose également que des problèmes tels que les problèmes de débordement / enveloppement ont déjà été traités.

Mise à jour: Visual Studio 6.0 n'a réellement pas stdint.h comme suggéré par Christoph. Un peu de benchmarking sur Windows (VS 6.0, débogage, 32 bits) avec une poignée de boucles empilées donne int et long comme fournissant des performances similaires, qui est environ deux fois plus que rapide comme char . Exécuter le même test sur Linux avec GCC PEGS INT et LONG comme similaire, et à la fois plus rapide que Char , bien que la différence soit moins prononcée.

Comme note latérale, je n'ai pas passé beaucoup de temps à regarder, mais La première implémentation de stdint.h pour vs 6.0 j'ai trouvé (via Wikipedia ) Définit uint_fast8_t comme non signé , malgré cela semblant être plus lent dans mes tests au moins. Ainsi, la morale de l'histoire, comme Christoph suggéré à juste titre: toujours de référence!


0 commentaires

6 Réponses :


6
votes

Eh bien, le premier problème est qu'il n'est pas défini par la standard C si un clair char est signé ou non signé - la seule plage que vous pouvez contenir de 0 à 127.

autre que cela, en général int est censé être le type correspondant à la taille du mot native de l'architecture (mais cela n'est bien sûr appliqué par rien). Cela aurait tendance à être le type avec la meilleure performance arithmétique, mais c'est à peu près tout ce que vous pouvez dire.

Notez que les opérandes plus étroits que int sont élargis soit à int ou non signé INT lors de l'évaluation d'expression de toute façon.


4 commentaires

Oui, on m'a dit que A INT a la taille d'un registre de processeur, donc si vous utilisez un caractère, le registre ne sera pas plein mais utilisera toujours le même temps pour faire des opérations basculaires être le registre totalement utilisé ou non.


De plus, INT8_T et UINT8_T seraient de meilleurs types à utiliser que de caractères.


@AIF: Autant que je sache, cela prend même plus le temps de faire la conversion haut / bas.


Je suis tombé sur ces 2: << a href = "http://www.evenenthelix.com/realtimantra/basics/optimizingcandcppcode.htm#prefer" rel = "NOWollow NOREFERRER"> EventHelix.com/realtiMemantra/basics/... INT sur char et court> et << a href = "http://fr.wikibooks.org/wiki/optimizizing_c%2b%2b/writing_efficient_code/performance_improving_features" rel = "Nofollow NOREFERRER"> EN.WIKIBOOKS.ORG/ wiki / optimizizizing_c% 2b% 2b / écrit_efficient_ code / ... > Mais je ne considère pas non plus autoritaire. Désolé, pas de preuve dure, ressemble plus à l'anecdotique.



0
votes

Le contenu principal que je verrais, c'est que votre code utilise un type qui signifie une chose pour les valeurs qui signifient autre chose - par exemple, il existe un problème sémantique qui pourrait être un problème de maintenance. Si vous l'avez fait, je vous recommanderais probablement de tapeDefing: xxx

de cette façon, a) il est plus clair ce que vous faites, et b) Vous pouvez le changer facilement (par exemple, juste le Un endroit) si vous rencontrez des ennuis.

Avez-vous un vraiment bonne raison ne pas utiliser int ?


1 commentaires

Votre point sémantique est déjà utilisé dans le seul endroit que j'ai vu cela implémenté. Et non, je n'ai aucune bonne raison, je ne suis que curieux de savoir si cela fait une différence.



3
votes

Un autre con je peux penser est que (autant que je sache) les processeurs "modernes" font tous leurs mathématiques dans des entiers "pleins", généralement 32 bits. Donc, traiter avec un char signifie généralement tirer un seul octet hors de la mémoire, remplissant de 0 dans le transfert à un registre, faisant quelque chose avec elle, puis serrant uniquement les bits les moins importants du résultat de la mémoire . Surtout si le Char n'est pas aligné sur une limite pratique, cet accès à la mémoire prend beaucoup plus de travail à accomplir.

Utilisation char pour int est vraiment utile lorsque vous avez un lot de chiffres (c'est-à-dire un grand tableau) et que vous devez conserver espace.


1 commentaires

Les caractères nécessitent moins de remplissages de la ligne de cache et sont mieux adaptés au cache.



2
votes

interne, les processeurs effectuent généralement des arithmétiques sur des mots de la machine. Cela signifie que lorsque lorsque des calculs sur d'autres types sont effectués, bien que le calcul lui-même prenait la même durée, en fonction de l'ensemble d'instructions disponibles, des travaux supplémentaires devraient être effectués pour lire les entrées et pour contraindre les résultats de calcul dans le type cible (par exemple. Signature / remplissage de zéro, changeant / masquage pour éviter les accès à la mémoire non alignée, etc.).

C'est pourquoi C définit les types et les opérations telles que c'est - la taille de int n'est pas mandatée par la norme, permettant aux auteurs du compilateur de le faire correspondre à un mot machine et l'évaluation de l'expression est définie. Pour promouvoir des types entier plus petits à INT , réduisant énormément le nombre de points à laquelle les résultats doivent être contraints à un type cible.

Raisons valides à utiliser Char pour stocker des valeurs entières sont lorsque l'espace compte vraiment que beaucoup (pas aussi souvent que vous pourriez penser), et lorsque vous décrivez un format / protocole de données externe que vous êtes marshalling données à / vers. Attendez-vous à des utilisations de Char pour inciter une légère perte de performances, en particulier sur le matériel tel que le SPU de cellules où seuls les accès à la mémoire de la taille des mots de la machine sont disponibles, il est donc nécessaire d'accéder à une charcuterie nécessite plusieurs changements et masques.


0 commentaires

3
votes

L'arithmétique sur les caractères sera presque certainement réellement effectué en utilisant les mêmes registres que l'arithmétique sur INTS. Par exemple: xxx

L'addition compile à ce qui suit avec VC ++: xxx

où EAX est un registre 32 bits.

Il n'y a donc pas d'adive d'utilisation des caractères sur InTS lorsqu'il s'agit de performances arithmétiques.


3 commentaires

Oui, mais n'a pas accès à un octet plus lent à cause des problèmes d'alignement? Le fait que l'accès soit une seule ligne d'ASM ne signifie pas qu'il n'y a pas plus de microcode exécuté. Avez-vous une idée de cela?


Coupable comme chargé peut-être trop de simplification sur "remplir avec 0" et "siguant dans un octet". Mais je crois que quelque chose comme ça se passe, à Quelques niveaux .


Je n'ai aucune idée de l'accès au bus (que je pense est le vrai problème) des transformateurs modernes, j'ai peur - je répondais à la question de la performance arithmétique.



12
votes

C99 a ajouté des types d'entiers «les plus rapides» de largeur minimale pour résoudre ce problème. Pour la plage qui vous intéresse, les types seraient int_fast8_t et uint_fast8_t , qui peut être trouvé dans stdint.h

Gardez à l'esprit qu'il pourrait ne pas y avoir de gain de performance (l'augmentation de la consommation de mémoire pourrait même réduire les choses); Comme toujours, repère! Ne pas optimiser prématurément ou uniquement sur des hypothèses potentiellement imparfaitantes de ce qui devrait fonctionner.


6 commentaires

Si vous vous souciez de la portabilité (comme je le fais où je partage le code entre cible et PC incorporé), n'utilisez pas de types rapides pour "stocker" quelque chose (membres de la classe, membres de la structure, etc.). Utilisez des types rapides pour itérus, des tableaux d'accès ou une variable de cache locale.


@Mar: L'utilisation de types d'entiers de taille fixe ne suffit pas à garantir que les structures peuvent être sérialisées et désérialisées sur des architectures en raison de l'ordre de rembourrage et d'octet; Comme vous devrez donc sérialiser les membres de la structure de manière séquentielle de toute façon, je ne pense pas qu'il n'y ait rien de mal à utiliser des types rapides pour les variables des membres


@Christoph: Eh bien, c'est non seulement la sérialisation, mais vrai - ma déclaration était trop forte. Cela devrait être plutôt «faire attention à ce sujet» comme une taille accrue peut vous frapper fort dans certains scénarios.


L'analyse comparative est certainement la morale de cette histoire; Je viens de mettre à jour la question avec les résultats d'une benchmarking que j'ai faite. Notamment, la mise en œuvre de STDINT.H que j'ai trouvé pour Visual Studio définit uint_fast8_t comme quelque chose qui est loin du type le plus rapide de mes tests.


Un majeur gotcha avec les types STDINT.H est qu'il existe à toutes fins pratiques, aucune garantie de lorsque les mathématiques entre les types signés et non signés produiront un résultat signé ou non signé (car cela est déterminé par la taille de la taille des tailles de les types et la question, plutôt que les types utilisés, et si un savait la taille des types en question, on n'aurait pas besoin des types comme uint_fast8_t dans le premier lieu). Par exemple, la somme d'un int32_t et a uint_fast8_t peut être un entier 32 bits signé ou non signé, et (sur des machines à 8 ou 16 bits) le produit de Deux uint8_fast8_t valeurs ...


... pourrait être un signé de 16 bits int qui ne serait pas en mesure de contenir des résultats supérieurs à 32767.