Quel est l'avantage du format little endian?

140

Les processeurs Intel (et peut-être quelques autres) utilisent le format little endian pour le stockage.

Je me demande toujours pourquoi quelqu'un voudrait stocker les octets dans l'ordre inverse. Ce format présente-t-il des avantages par rapport au format big endian?

Biscuit salé
la source
1
Le 6502 était un premier (le premier?) Processeur en pipeline. Je crois me souvenir que certains ont prétendu qu’il était peu important pour un problème lié aux performances en raison du pipeline - mais je n’ai pas la moindre idée de ce que ce problème aurait pu être. Aucune suggestion?
Steve314
1
@ Steve314: Ma réponse explique à quel point Endian contribue peu aux performances d'un processeur en pipeline: programmers.stackexchange.com/q/95854/27874
Martin Vilcans
3
Little-endian, big-endian - vous devez choisir l’un ou l’autre. C'est comme conduire à gauche ou à droite de la route.
3
Je vous suggère d'écrire du code en ASM, de préférence pour une architecture "old school" telle que 6502 ou Z80. Vous verrez immédiatement pourquoi ceux-ci utilisent le petit endian. Les architectures qui utilisent big endian ont certaines caractéristiques dans leur jeu d'instructions qui rendent ce format préférable. Ce n'est pas une décision arbitraire à prendre!
Stefan Paul Noack
2
Chaque système de commande d’octets a ses avantages. Les machines little-endian vous permettent de lire l'octet le plus bas en premier, sans lire les autres. Vous pouvez vérifier si un nombre est impair ou pair (le dernier bit est 0) très facilement, ce qui est bien si vous aimez ce genre de chose. Les systèmes big-endian stockent les données en mémoire de la même manière que nous, les humains, pensent des données (de gauche à droite), ce qui facilite le débogage à bas niveau.
Koray Tugay

Réponses:

198

Il y a des arguments dans les deux sens, mais un point est que, dans un système little-endian, l'adresse d'une valeur donnée en mémoire, d'une largeur de 32, 16 ou 8 bits, est la même.

En d'autres termes, si vous avez en mémoire une valeur de deux octets:

0x00f0   16
0x00f1    0

prendre ce '16' comme valeur 16 bits (c 'short' sur la plupart des systèmes 32 bits) ou comme valeur 8 bits (généralement c 'char') ne change que l'instruction d'extraction que vous utilisez - pas l'adresse que vous extrayez de.

Sur un système big-endian, avec ce qui précède, présenté comme suit:

0x00f0    0
0x00f1   16

vous devrez incrémenter le pointeur, puis effectuer l'opération d'extraction plus étroite sur la nouvelle valeur.

En bref, "sur les systèmes little endian, le casting n’est pas une opération".

jimwise
la source
3
En supposant, bien sûr, que les octets de poids fort que vous n'avez pas lus puissent être raisonnablement ignorés (par exemple, vous savez qu'ils sont nuls de toute façon).
Steve314
10
@ Steve314: Si je suis en C, le downcasting de 32 à 16 bits (par exemple) sur un système à complément de 2 - la grande majorité des systèmes - les octets n'ont pas besoin d'être nuls pour être ignorés. Peu importe leur valeur, je peux les ignorer et rester conforme aux attentes du standard C et du programmeur.
9
@ Stritzinger - nous parlons du code assembleur / machine généré par un compilateur, qui ne peut pas être portable. Le code de langage de niveau supérieur à compiler est portable - il compile simplement différentes opérations sur les différentes architectures (comme le font tous les opérateurs).
jimwise
7
Je n'achète pas cet argument, car sur les architectures big-endian, un pointeur pourrait indiquer la fin, plutôt que le début, de tout ce à quoi vous faites référence et que vous auriez exactement le même avantage.
dan_waterworth
4
@dan_waterworth pas tout à fait - gardez à l'esprit les règles arithmétiques de pointeur en C, par exemple, et ce qu'il se passe lorsque vous incrémentez ou décrémentez les conversions du même pointeur. Vous pouvez déplacer la complexité, mais vous ne pouvez pas l'éliminer.
jimwise
45

Je me demande toujours pourquoi quelqu'un voudrait stocker les octets dans l'ordre inverse.

Le big-endian et le petit-endian ne sont qu'un "ordre normal" et un "ordre inverse" d'un point de vue humain, et seulement si tout cela est vrai ...

  1. Vous lisez les valeurs à l'écran ou sur papier.
  2. Vous mettez les adresses de mémoire inférieures à gauche et les adresses supérieures à droite.
  3. Vous écrivez en hex, avec le nybble d'ordre supérieur à gauche, ou binaire, avec le bit le plus significatif à gauche.
  4. Vous lisez de gauche à droite.

Ce sont toutes des conventions humaines qui ne comptent pas du tout pour un processeur. Si vous deviez conserver les numéros 1 et 2 et le flip 3, le petit-endian semblerait «parfaitement naturel» aux personnes qui lisent l'arabe ou l'hébreu, qui sont écrites de droite à gauche.

Et il y a d'autres conventions humaines qui font du big-endian qui ne semblent pas naturelles, comme ...

  • L'octet "le plus élevé" (le plus élevé) doit se trouver à l'adresse mémoire "la plus élevée".

À l'époque où je programmais principalement 68K et PowerPC, je considérais le big-endian comme étant "correct" et le petit-endian comme étant "faux". Mais comme je travaille davantage avec ARM et Intel, je me suis habitué au petit-endian. Cela n'a pas d'importance.

Bob Murphy
la source
30
Les nombres sont en fait écrits du [chiffre le plus significatif] de gauche au [chiffre du moins significatif] à droite en arabe et en hébreu.
Random832
5
Alors pourquoi les bits d'un octet sont-ils stockés au format "big endian"? Pourquoi ne pas être cohérent?
tskuzzy
11
Ils ne le sont pas - le bit 0 est par convention le moins significatif, et le bit 7 le plus significatif. De plus, vous ne pouvez généralement pas placer d'ordre sur les bits d'un octet, car les bits ne sont pas adressables individuellement. Bien sûr, ils peuvent avoir un ordre physique dans un protocole de communication ou un support de stockage donné, mais à moins que vous ne travailliez au niveau du protocole ou du matériel de bas niveau, vous n'avez pas besoin de vous préoccuper de cet ordre.
Stewart
3
BlueRaja: uniquement par convention d'écriture sur papier. Cela n'a rien de commun avec l'architecture du processeur. Vous pouvez écrire l'octet sous la forme 0-7 LSB-MSB au lieu de 7-0 MSB-LSB et rien ne change du point de vue de l'algorithme.
SF.
2
@SF .: "Push short, pop tout sauf court " vous surprendra quand même. Même si vous ne corrompez pas la pile en poussant des octets, vous ne faites jamais apparaître ou vice-versa ... x86 (32 bits), par exemple, veut vraiment que la pile soit alignée sur un mot-symbole et Le pointeur de pile ne pas être un multiple de 4 peut entraîner des problèmes d'alignement. Et même si ce n'était pas le cas, les choses ont poussé mot / mot / mot / mot / etc à la fois - donc l'octet de poids faible sera toujours le premier que vous obtiendrez lorsque vous ferez apparaître une note.
cHao
41

OK, voici la raison qui m’a été expliquée: Addition et soustraction

Lorsque vous ajoutez ou soustrayez des nombres sur plusieurs octets, vous devez commencer par l'octet le moins significatif. Si vous ajoutez deux nombres de 16 bits, par exemple, il peut y avoir un report de l'octet le moins significatif à l'octet le plus significatif. Vous devez donc commencer par l'octet le moins significatif pour voir s'il y a un report. C'est la même raison pour laquelle vous commencez par le chiffre le plus à droite lorsque vous effectuez une addition longue. Vous ne pouvez pas partir de la gauche.

Considérons un système 8 bits qui récupère les octets de manière séquentielle à partir de la mémoire. S'il extrait en premier l' octet le moins significatif , il peut commencer à faire l'addition pendant que l'octet le plus significatif est extrait de la mémoire. Ce parallélisme est la raison pour laquelle les performances sont meilleures dans les versions plus petites, telles que les systèmes. S'il devait attendre que les deux octets soient extraits de la mémoire ou les récupérer dans l'ordre inverse, cela prendrait plus de temps.

Ceci est sur les anciens systèmes 8 bits. Sur un processeur moderne, je doute que l’ordre des octets fasse toute la différence et nous utilisons little endian uniquement pour des raisons historiques.

Martin Vilcans
la source
3
Ah, c'est donc à peu près la même raison pour laquelle j'utilise l'ordre des morceaux en petit-endian pour les grands entiers. J'aurais dû régler ça. Les gens ont vraiment besoin de travailler sur la cybernétique maintenant - mon cerveau a déjà désespérément besoin de pièces de rechange et de mises à niveau radicales, je ne peux pas attendre pour toujours!
Steve314
2
Une pensée - le 6502 ne faisait pas beaucoup de calculs 16 bits dans le matériel - c’était, après tout, un processeur 8 bits. Mais il a fait un adressage relatif, en utilisant des décalages signés sur 8 bits par rapport à une adresse de base sur 16 bits.
Steve314
2
Notez que cette idée est toujours importante pour l'arithmétique des entiers à précision multiple (comme l'a dit Steve314), mais au niveau du mot. Maintenant, la plupart des opérations ne sont pas directement affectées par l’endianité du processeur: on peut toujours stocker le mot le moins significatif en premier sur un système big endian, comme le fait GMP. Les processeurs little-endian ont toujours un avantage pour les quelques opérations (par exemple, certaines conversions de chaînes?) Qui pourrait être plus facile en lisant un octet à la fois, puisque l'ordre sur les octets de ces nombres est correct uniquement sur un système little-endian.
vinc17
Les processeurs little-endian présentent un avantage lorsque la bande passante mémoire est limitée, comme dans certains processeurs ARM 32 bits avec bus mémoire 16 bits ou le 8088 avec bus de données 8 bits: le processeur peut simplement charger la moitié basse add / sub / mul ... avec elle en attendant la moitié supérieure
phuclv
13

Avec les processeurs 8 bits, il était certainement plus efficace, vous pouvez effectuer une opération 8 ou 16 bits sans avoir besoin de code différent ni de mettre en tampon des valeurs supplémentaires.

C'est encore mieux pour certaines opérations d'addition si vous traitez un octet à la fois.

Mais il n'y a aucune raison pour que big-endian soit plus naturel - en anglais, vous utilisez treize (petit endian) et vingt-trois (big endian)

Martin Beckett
la source
1
Big-endian est en effet plus facile pour les humains car il ne nécessite pas de réarranger les octets. Par exemple, sur un PC, 0x12345678est stocké comme 78 56 34 12alors que sur un système BE, c’est 12 34 56 78(l’octet 0 est à gauche, l’octet 3 à droite). Notez que plus le nombre est grand (en bits), plus il nécessite d’échange; un mot nécessiterait un échange; un DWORD, deux passes (trois swaps totaux); un mot QWORD trois passes (7 au total), et ainsi de suite. C'est-à-dire des (bits/8)-1échanges. Une autre option est de les lire en avant et en arrière (lire chaque octet en avant, mais en balayant tout le # en arrière).
Synetech
Cent treize est soit du milieu, soit du big-endian, "treize" étant essentiellement un chiffre non décimal. Lorsque nous épelons des chiffres, il y a quelques écarts mineurs par rapport aux conventions de base constante que nous utilisons pour les chiffres, mais une fois que vous supprimez ces cas spéciaux, le reste est big-endian - des millions avant des milliers, des milliers avant des centaines, etc.
Steve314
@ Synetech- heureusement, l'ordinateur n'a pas à se soucier de la façon dont les humains les lisent. C'est comme si on disait que le flash NAND est meilleur parce qu'autre chose
Martin Beckett
1
@ Steve314, les mots de chiffres épelés importent peu, c'est l'affichage numérique qui est utilisé lors de la programmation. Martin, aucun ordinateur n'a à se préoccuper de la façon dont les humains lisent les chiffres, mais s'il est facile pour eux de les lire, la programmation (ou tout autre travail connexe) devient plus facile et certains défauts et bogues peuvent être réduits ou évités.
Synetech
@ steve314 Et en danois, "95" se prononce "fem halvfems" (cinq, plus quatre ans et demi).
Vatine
7

La convention de date japonaise est "big endian" - aaaa / mm / jj. C'est pratique pour les algorithmes de tri, qui peuvent utiliser une simple comparaison de chaîne avec la règle habituelle du premier caractère est le plus significatif.

Quelque chose de similaire s'applique aux nombres big-endian stockés dans un enregistrement de champ le plus significatif. L'ordre de signification des octets dans les champs correspond à la signification des champs dans l'enregistrement. Vous pouvez donc utiliser a memcmppour comparer les enregistrements sans vous soucier de savoir si vous comparez deux mots longs, quatre mots ou huit octets distincts.

Si vous inversez l'ordre de signification des champs, vous obtenez le même avantage, mais pour les nombres little-endian plutôt que big-endian.

Cela a bien sûr très peu de signification pratique. Que votre plate-forme soit big-endian ou little-endian, vous pouvez commander des champs d'enregistrements pour exploiter cette astuce si vous en avez vraiment besoin. C'est juste pénible si vous devez écrire du code portable .

Je peux aussi bien inclure un lien vers l'appel classique ...

http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt

MODIFIER

Une pensée supplémentaire. Une fois, j'ai écrit une grande bibliothèque de nombres entiers (pour voir si je pouvais le faire), et pour cela, les morceaux de 32 bits sont stockés dans un ordre little-endian, quelle que soit la manière dont la plate-forme ordonne les bits dans ces morceaux. Les raisons étaient ...

  1. De nombreux algorithmes commencent tout naturellement à fonctionner aux extrémités les moins significatives et souhaitent que ces extrémités soient mises en correspondance. Par exemple, les portées se propagent à des chiffres de plus en plus significatifs, il est donc logique de commencer par la fin la moins significative.

  2. Augmenter ou réduire une valeur signifie simplement ajouter / supprimer des morceaux à la fin - pas besoin de décaler des morceaux vers le haut ou le bas. La copie peut encore être nécessaire en raison de la réallocation de la mémoire, mais pas souvent.

Bien entendu, cela n’a aucune pertinence pour les processeurs - jusqu’à ce que les processeurs soient conçus avec un support matériel gros-entier, c’est une pure bibliothèque.

Steve314
la source
7

Personne d'autre n'a répondu POURQUOI cela pourrait être fait, beaucoup de choses sur les conséquences.

Prenons un processeur 8 bits capable de charger un seul octet de la mémoire dans un cycle d'horloge donné.

Maintenant, si vous voulez charger une valeur de 16 bits dans (par exemple) le registre unique et unique de 16 bits que vous avez - c’est-à-dire le compteur de programme, un moyen simple de le faire est:

  • Charger un octet à partir de l'emplacement de récupération
  • décaler cet octet vers la gauche 8 places
  • incrémente l'emplacement de récupération de mémoire de 1
  • charger l'octet suivant (dans la partie basse du registre)

le résultat: vous n'incrémentez jamais uniquement l'emplacement de recherche, vous ne chargez que dans la partie inférieure de votre registre le plus large, et il vous suffit de pouvoir passer à gauche. (Bien sûr, le déplacement à droite est utile pour les autres opérations, donc celle-ci est un peu un spectacle parallèle.)

La conséquence en est que les éléments 16 bits (double octet) sont stockés dans l’ordre Most..Least. C'est-à-dire que l'adresse la plus petite a l'octet le plus significatif - le gros endian.

Si vous essayez plutôt de charger avec little endian, vous devrez charger un octet dans la partie inférieure de votre registre large, puis charger l'octet suivant dans une zone intermédiaire, le déplacer, puis le placer en haut de votre registre plus large. . Ou utilisez un arrangement plus complexe de gating pour pouvoir charger sélectivement dans l'octet supérieur ou inférieur.

En essayant d’aller un peu en arrière, vous avez besoin de plus de silicium (commutateurs et portes) ou de plus d’opérations.

En d’autres termes, en ce qui concerne le rapport qualité-prix, jadis, vous obtenez plus de rendement pour la plupart des performances et la plus petite surface de silicium.

De nos jours, ces considérations sont à peu près hors de propos, mais des choses comme le remplissage de pipeline peuvent encore être un gros problème.

Quand il s’agit d’écrire s / w, la vie est souvent plus facile avec l’adressage little endian.

(Et les grands processeurs endian ont tendance à être grand endian en termes de l' ordre des octets et peu endian en termes de bits-en-octets. Mais certains processeurs sont étranges et utiliseront grand peu endian la commande ainsi que l' ordre des octets. Cela rend la vie très intéressant pour le concepteur h / w qui ajoute des périphériques mappés en mémoire mais n’a aucune autre conséquence pour le programmeur.)

Rapidement
la source
3

jimwise a fait valoir un bon point. Il y a un autre problème, en little endian vous pouvez faire ce qui suit:

byte data[4];
int num=0;
for(i=0;i<4;i++)
    num += data[i]<<i*8; 

OR 

num = *(int*)&data; //is interpreted as

mov dword data, num ;or something similar it has been some time

Plus simple pour les programmeurs qui ne sont pas affectés par l'inconvénient évident des emplacements échangés en mémoire. Personnellement, je trouve que le big endian est l’inverse de ce qui est naturel :). 12 devrait être stocké et écrit comme 21 :)

Cem Kalyoncu
la source
1
Cela prouve simplement qu'il est plus rapide / plus simple de travailler dans n'importe quel format natif de la CPU. Cela ne dit rien si c'est mieux. La même chose vaut pour le big endian: for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }correspond à move.l data, numun processeur big endian.
Martin Vilcans
@martin: une soustraction de moins est préférable dans mon livre
Cem Kalyoncu
Cela n'a pas vraiment d'importance car le compilateur déroulera la boucle de toute façon. Dans tous les cas, de nombreux processeurs disposent d'instructions de permutation d'octets pour gérer ce problème.
Martin Vilcans
je ne suis pas d'accord avec bcoz sur le gros endian, je ferais {num << = 8; num | = data [i]; } au moins, il n'est pas nécessaire de calculer le décompte du quart gauche en utilisant mul
Hayri Uğur Koltuk le
@ali: votre code fera l'opération exacte que j'ai écrite et ne fonctionnera pas sur le Big Endian.
Cem Kalyoncu
1

Je me demande toujours pourquoi quelqu'un voudrait stocker les octets dans l'ordre inverse

Le nombre décimal est écrit big endian. C’est aussi la façon dont vous l’écrivez en anglais. Vous commencez par le chiffre le plus significatif, puis le chiffre le plus significatif au moins significatif. par exemple

1234

est mille deux cent trente quatre.

C'est ainsi que big endian est parfois appelé l'ordre naturel.

En little endian, ce nombre serait un, vingt, trois cent quatre mille.

Cependant, lorsque vous effectuez une opération arithmétique telle qu'une addition ou une soustraction, vous commencez par la fin.

  1234
+ 0567
  ====

Vous commencez avec 4 et 7, écrivez le chiffre le plus bas et rappelez-vous le report. Ensuite, vous ajoutez 3 et 6, etc. Pour ajouter, soustraire ou comparer, il est plus simple à implémenter, si vous avez déjà la logique de lire la mémoire dans l’ordre, si les nombres sont inversés.

Pour prendre en charge big endian de cette façon, vous avez besoin d'une logique pour lire la mémoire à l'envers ou vous avez un processus RISC qui n'opère que sur des registres. ;)

Une grande partie de la conception Intel x86 / Amd x64 est historique.

Peter Lawrey
la source
0

Le big-endian est utile pour certaines opérations (comparaisons de "bignums" d'égales longueurs d'octets). Little-endian pour les autres (en ajoutant éventuellement deux "bignums"). En fin de compte, cela dépend de la configuration du matériel de la CPU, il s’agit généralement de l’une ou de l’autre (certaines puces MIPS étaient, IIRC, commutables au démarrage pour être LE ou BE).

Vatine
la source
0

Lorsque seuls le stockage et le transfert avec des longueurs variables sont impliqués, sans arithmétique à valeurs multiples, alors LE est généralement plus facile à écrire, tandis que BE est plus facile à lire.

Prenons une conversion entre chaînes (et retour) comme exemple spécifique.

int val_int = 841;
char val_str[] = "841";

Lorsque l'int est converti en chaîne, le chiffre le moins significatif est plus facile à extraire que le chiffre le plus significatif. Tout peut être fait dans une boucle simple avec une condition de fin simple.

val_int = 841;
// Make sure that val_str is large enough.

i = 0;
do // Write at least one digit to care for val_int == 0
{
    // Constants, can be optimized by compiler.
    val_str[i] = '0' + val_int % 10;
    val_int /= 10;
    i++;
}
while (val_int != 0);

val_str[i] = '\0';
// val_str is now in LE "148"
// i is the length of the result without termination, can be used to reverse it

Maintenant, essayez la même chose dans l'ordre BE. Vous avez généralement besoin d'un autre diviseur qui détient la plus grande puissance de 10 pour le nombre spécifique (ici 100). Vous devez d’abord trouver ceci, bien sûr. Beaucoup plus de choses à faire.

La conversion de chaîne en int est plus facile à faire dans BE, lorsqu'elle est effectuée en tant qu'opération d'écriture inversée. Write stocke le dernier chiffre le plus significatif, il doit donc être lu en premier

val_int = 0;
length = strlen(val_str);

for (i = 0; i < length; i++)
{
    // Again a simple constant that can be optimized.
    val_int = 10*val_int + (val_str[i] - '0');
}

Maintenant, faites la même chose dans l'ordre. Encore une fois, vous auriez besoin d'un facteur supplémentaire commençant par 1 et multiplié par 10 pour chaque chiffre.

Ainsi, je préfère généralement utiliser BE pour le stockage, car une valeur est écrite exactement une fois, mais lue au moins une fois et peut-être plusieurs fois. Pour sa structure plus simple, j’utilise aussi généralement la route pour convertir en LE puis inverser le résultat, même s’il écrit la valeur une seconde fois.

Un autre exemple de stockage BE serait le codage UTF-8, et bien d’autres.

Sécurise
la source