Qual é a vantagem do formato little endian?

140

Os processadores Intel (e talvez alguns outros) usam o formato little endian para armazenamento.

Eu sempre me pergunto por que alguém iria querer armazenar os bytes na ordem inversa. Esse formato tem vantagens sobre o formato big endian?

Biscoito
fonte
1
O 6502 foi um processador pipeline inicial (o primeiro?). Parece que me lembro de alguma afirmação de que ela é pouco endianista para algum problema relacionado ao desempenho devido ao pipeline - mas não tenho idéia agora de que problema poderia ter sido. Alguma sugestão?
Steve314
1
@ Steve314: Minha resposta explica como pouco endian contribui com o desempenho em uma CPU pipelined: programmers.stackexchange.com/q/95854/27874
Martin Vilcans
3
Little-endian, big-endian - você deve escolher um ou outro. Como dirigir no lado esquerdo ou direito da estrada.
3
Eu sugiro que você escreva algum código no ASM, de preferência para uma arquitetura "antiga", como 6502 ou Z80. Você verá imediatamente por que eles usam little endian. As arquiteturas que usam big endian têm certas características em seu conjunto de instruções que tornam esse formato preferível. Não é uma decisão arbitrária a ser tomada!
Stefan Paul Noack 15/05
2
Cada sistema de ordem de bytes tem suas vantagens. Máquinas little-endian permitem que você leia primeiro o byte mais baixo, sem ler os outros. Você pode verificar se um número é ímpar ou par (o último bit é 0) com muita facilidade, o que é legal se você gosta desse tipo de coisa. Os sistemas big endian armazenam dados na memória da mesma maneira que os humanos pensam sobre dados (da esquerda para a direita), o que facilita a depuração de baixo nível.
Koray Tugay

Respostas:

198

Existem argumentos de qualquer maneira, mas um ponto é que, em um sistema little endian, o endereço de um determinado valor na memória, tomado como largura de 32, 16 ou 8 bits, é o mesmo.

Em outras palavras, se você tiver na memória um valor de dois bytes:

0x00f0   16
0x00f1    0

tomar esse '16' como um valor de 16 bits (c 'curto' na maioria dos sistemas de 32 bits) ou como um valor de 8 bits (geralmente c 'char') altera apenas a instrução de busca que você usa - não o endereço que você busca de.

Em um sistema big endian, com o exposto acima, é apresentado como:

0x00f0    0
0x00f1   16

você precisaria incrementar o ponteiro e, em seguida, executar a operação de busca mais estreita no novo valor.

Então, resumindo, 'em pequenos sistemas endianos, os elencos não são opcionais'.

jimwise
fonte
3
Supondo, é claro, que os bytes de alta ordem que você não leu possam ser razoavelmente ignorados (por exemplo, você sabe que eles são zero de qualquer maneira).
Steve314
10
@ Steve314: Se eu estiver em downcasting C de 32 a 16 bits (por exemplo) em um sistema de complemento de 2 - a grande maioria dos sistemas - os bytes não precisam ser zero para serem ignorados. Independentemente do seu valor, posso ignorá-los e permanecer em conformidade com o padrão C e as expectativas do programador.
9
@ Stritzinger - estamos falando sobre o código de montagem / máquina gerado por um compilador, que não pode ser portátil. O código de linguagem de nível superior a ser compilado é portátil - ele apenas compila para diferentes operações nas diferentes arquiteturas (como todas as operações).
jimwise
7
Eu não compreendo esse argumento, porque nas arquiteturas big-endian, um ponteiro pode apontar para o final, e não o começo, de qualquer coisa a que você está se referindo e que você teria exatamente a mesma vantagem.
dan_waterworth
4
@dan_waterworth não é bem assim - lembre-se das regras aritméticas dos ponteiros em C, por exemplo, e o que acontece quando você incrementa ou diminui as projeções do mesmo ponteiro. Você pode mover a complexidade, mas não pode eliminá-la.
jimwise
45

Eu sempre me pergunto por que alguém iria querer armazenar os bytes na ordem inversa.

Big endian e little endian são apenas "ordem normal" e "ordem inversa" de uma perspectiva humana, e somente se tudo isso for verdade ...

  1. Você está lendo os valores na tela ou no papel.
  2. Você coloca os endereços de memória inferiores à esquerda e os endereços mais altos à direita.
  3. Você está escrevendo em hexadecimal, com o nybble de alta ordem à esquerda ou binário, com o bit mais significativo à esquerda.
  4. Você lê da esquerda para a direita.

Essas são todas as convenções humanas que não importam para a CPU. Se você mantivesse os itens 1 e 2 e virasse o número 3, o little-endian pareceria "perfeitamente natural" para as pessoas que lêem árabe ou hebraico, escritas da direita para a esquerda.

E existem outras convenções humanas que tornam o big endian algo antinatural, como ...

  • O byte "mais alto" (mais significativo) deve estar no endereço de memória "mais alto".

Quando eu estava programando principalmente 68K e PowerPC, considerava o big endian "certo" e o little endian "errado". Mas desde que venho fazendo mais trabalhos com ARM e Intel, me acostumei com o little-endian. Realmente não importa.

Bob Murphy
fonte
30
Na verdade, os números são escritos do [dígito mais significativo] da esquerda para o [dígito menos significativo] à direita em árabe e hebraico.
Random832
5
Então, por que os bits dentro de um byte são armazenados no formato "big endian"? Por que não ser consistente?
tskuzzy
11
Eles não são - o bit 0 é por convenção o menos significativo e o bit 7 é o mais significativo. Além disso, você geralmente não pode fazer pedidos de bits dentro de um byte, pois os bits não são endereçáveis ​​individualmente. Obviamente, eles podem ter uma ordem física em um determinado protocolo de comunicação ou mídia de armazenamento, mas, a menos que você esteja trabalhando no nível de protocolo ou hardware de baixo nível, não precisa se preocupar com esse pedido.
Stewart
3
BlueRaja: somente por convenção de escrita em papel. Isso não tem nada em comum com a arquitetura da CPU. Você pode escrever o byte como 0-7 LSB-MSB em vez de 7-0 MSB-LSB e nada muda do ponto de vista do algoritmo.
SF.
2
@SF .: "Pressione curto, pop qualquer coisa, menos curto ", você terá uma surpresa de qualquer maneira. Mesmo que você não esteja corrompendo a pilha pressionando bytes, você nunca aparece ou vice-versa ... x86 (32 bits), por exemplo, realmente deseja que a pilha esteja alinhada com dword e pressionando ou aparecendo qualquer coisa que cause o erro O ponteiro da pilha para não ser um múltiplo de 4 pode causar problemas de alinhamento. E mesmo que não funcionasse, as coisas pressionavam uma palavra inteira / dword / qword / etc de cada vez - então o byte baixo ainda será o primeiro a ser obtido quando você pop.
cHao 26/07
41

OK, aqui está o motivo, como eu já havia explicado: adição e subtração

Ao adicionar ou subtrair números de vários bytes, é necessário começar com o byte menos significativo. Se você estiver adicionando dois números de 16 bits, por exemplo, pode haver uma transferência do byte menos significativo para o byte mais significativo; portanto, você deve começar com o byte menos significativo para verificar se há uma transferência. Esse é o mesmo motivo pelo qual você inicia com o dígito mais à direita ao fazer a adição à mão. Você não pode começar da esquerda.

Considere um sistema de 8 bits que busca bytes seqüencialmente da memória. Se buscar primeiro o byte menos significativo , ele poderá começar a adição enquanto o byte mais significativo estiver sendo buscado na memória. Esse paralelismo é o motivo pelo qual o desempenho é melhor em little endian em sistemas. Se tivesse que esperar até que os dois bytes fossem buscados da memória ou buscá-los na ordem inversa, levaria mais tempo.

Isso ocorre em sistemas antigos de 8 bits. Em uma CPU moderna, duvido que a ordem dos bytes faça alguma diferença e usamos pouco endian apenas por razões históricas.

Martin Vilcans
fonte
3
Ah - então é mais ou menos a mesma razão pela qual eu uso pedaços de pequenos endianos para grandes números inteiros. Eu deveria ter resolvido isso. As pessoas realmente precisam trabalhar com cibernética agora - meu cérebro já precisa desesperadamente de algumas peças de reposição e algumas atualizações radicais, mal posso esperar para sempre!
Steve314
2
Um pensamento - o 6502 não fazia muita matemática de 16 bits no hardware - era, afinal, um processador de 8 bits. Mas fez o endereçamento relativo, usando deslocamentos assinados de 8 bits em relação a um endereço base de 16 bits.
Steve314
2
Observe que essa idéia ainda é importante para a aritmética inteira de precisão múltipla (como dito por Steve314), mas no nível da palavra. Agora, a maioria das operações não é diretamente afetada pela resistência do processador: ainda é possível armazenar a palavra menos significativa primeiro em um sistema big-endian, como é feito pelo GMP. Os processadores little-endian ainda têm uma vantagem para as poucas operações (por exemplo, algumas conversões de string?) Que poderiam ser mais fáceis de ler um byte de cada vez, uma vez que somente em um sistema little-endian, a ordem de bytes desses números está correta.
precisa saber é
os processadores little-endian têm uma vantagem no caso de a largura de banda da memória ser limitada, como em alguns processadores ARM de 32 bits com barramento de memória de 16 bits ou no 8088 com barramento de dados de 8 bits: o processador pode simplesmente carregar a metade baixa e fazê-lo adicionar / sub / mul ... com ele enquanto aguardava o maior meia
phuclv
13

Com processadores de 8 bits, certamente era mais eficiente, era possível executar uma operação de 8 ou 16 bits sem a necessidade de código diferente e sem a necessidade de armazenar buffer em valores extras.

Ainda é melhor para algumas operações de adição se você estiver lidando com um byte de cada vez.

Mas não há razão para que big-endian seja mais natural - em inglês você usa treze (little endian) e vinte e três (big endian)

Martin Beckett
fonte
1
O big endian é realmente mais fácil para os seres humanos, porque não requer reorganizar os bytes. Por exemplo, em um PC, 0x12345678é armazenado como 78 56 34 12em um sistema BE 12 34 56 78(o byte 0 está à esquerda, o byte 3 está à direita). Observe como quanto maior o número (em termos de bits), mais trocas são necessárias; uma PALAVRA exigiria uma troca; um DWORD, dois passes (três swaps totais); um QWORD três passes (7 no total) e assim por diante. Ou seja, (bits/8)-1swaps. Outra opção é lê-los tanto para a frente e para trás (lendo cada byte para a frente, mas a digitalização de todo o # para trás).
Synetech
Cento e treze é ou endian médio, ou então big endian com "treze" sendo essencialmente um dígito não decimal. Quando escrevemos números, há alguns desvios menores das convenções de base constante que usamos para dígitos, mas depois que você remove esses casos especiais, o resto é big-endian - milhões antes de milhares, milhares antes de centenas etc.
Steve314
@ Synetech- felizmente o computador não precisa se importar com a forma como os humanos os lêem. Isso é como alegar que o flash NAND é melhor porque ot '
Martin Beckett
1
@ Steve314, as palavras escritas dos números não importam, é a leitura numérica que é o que usamos quando programamos. Martin, nenhum computador não precisa se preocupar com a maneira como os humanos lêem os números, mas se for fácil para os humanos lerem, a programação (ou outro trabalho relacionado) se torna mais fácil e algumas falhas e erros podem ser reduzidos ou evitados.
Synetech
@ steve314 E em dinamarquês, "95" é pronunciado "fem halvfems" (cinco, mais vinte e quatro e meia).
Vatine 12/11/12
7

A convenção japonesa de datas é "big endian" - aaaa / mm / dd. Isso é útil para algoritmos de classificação, que podem usar uma comparação simples de cadeias com a regra usual de primeiro caractere é a mais significativa.

Algo semelhante se aplica aos números de big endian armazenados em um registro de campo mais significativo. A ordem de significância dos bytes nos campos corresponde à significância dos campos no registro; portanto, você pode usar a memcmppara comparar registros, sem se importar se está comparando duas palavras longas, quatro palavras ou oito bytes separados.

Inverta a ordem de significância dos campos e você obtém a mesma vantagem, mas para números little-endian em vez de big-endian.

Isso tem muito pouco significado prático, é claro. Se sua plataforma é big-endian ou little-endian, você pode solicitar campos de registros para explorar esse truque, se realmente precisar. É apenas uma dor se você precisar escrever um código portátil .

Posso incluir também um link para o apelo clássico ...

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

EDITAR

Um pensamento extra. Certa vez, escrevi uma grande biblioteca inteira (para ver se eu podia) e, para isso, os pedaços de 32 bits de largura são armazenados em ordem little-endian, independentemente de como a plataforma ordena os bits desses pedaços. As razões foram ...

  1. Muitos algoritmos simplesmente começam a funcionar no final menos significativo e desejam que esses fins sejam compatíveis. Por exemplo, além disso, os carregamentos propogam dígitos cada vez mais significativos, por isso faz sentido começar no final menos significativo.

  2. Aumentar ou diminuir um valor significa apenas adicionar / remover pedaços no final - não é necessário mudar os pedaços para cima / para baixo. Ainda é necessário copiar devido à realocação da memória, mas não com frequência.

Isso não tem relevância óbvia para os processadores, é claro - até que as CPUs sejam feitas com suporte a números grandes de hardware, isso é puramente uma coisa de biblioteca.

Steve314
fonte
7

Ninguém mais respondeu POR QUE isso pode ser feito, muitas coisas sobre consequências.

Considere um processador de 8 bits que pode carregar um único byte da memória em um determinado ciclo de clock.

Agora, se você deseja carregar um valor de 16 bits, digamos (digamos) no único e somente registrador de 16 bits que você possui - ou seja, no contador do programa, então uma maneira simples de fazer isso é:

  • Carregar um byte do local da busca
  • desloque esse byte para a esquerda 8 lugares
  • incrementa a localização da busca de memória em 1
  • carregar o próximo byte (na parte de baixa ordem do registro)

o resultado: você apenas incrementa o local da busca, apenas carrega na parte inferior do seu registro mais amplo e precisa mudar de posição para a esquerda. (É claro que mudar para a direita é útil para outras operações, portanto, este é um pouco de um show paralelo.)

Uma conseqüência disso é que o material de 16 bits (byte duplo) é armazenado na ordem Most..Least. Ou seja, o endereço menor tem o byte mais significativo - tão grande endian.

Se você tentou carregar usando little endian, seria necessário carregar um byte na parte inferior do seu registro amplo, depois carregar o próximo byte em uma área intermediária, alterá-lo e colocá-lo na parte superior do seu registro mais amplo . Ou use um arranjo mais complexo de portas para poder carregar seletivamente no byte superior ou inferior.

O resultado de tentar se tornar um pouco endian é que você precisa de mais silício (interruptores e portas) ou mais operações.

Em outras palavras, em termos de ganhar dinheiro por dinheiro nos velhos tempos, você ganha mais dinheiro pela maior performance e pela menor área de silício.

Hoje em dia, essas considerações são irrelevantes, mas coisas como preenchimento de pipeline ainda podem ser um grande problema.

Quando se trata de escrever em preto e branco, a vida é frequentemente mais fácil quando se usa pouco endereçamento endian.

(E os processadores big endian tendem a ser big endian em termos de ordenação de bytes e pouco endian em termos de bits em bytes. Mas alguns processadores são estranhos e usarão a ordenação de bits big endian e a ordem de bytes. Isso torna a vida muito interessante para o designer h / w adicionando periféricos mapeados na memória, mas não tem outra conseqüência para o programador.)

rapid_now
fonte
3

jimwise fez um bom argumento. Há outro problema: no little endian, você pode fazer o seguinte:

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

Mais direto para programadores que não são afetados pela desvantagem óbvia de locais trocados na memória. Pessoalmente, acho que o big endian é inverso do que é natural :). 12 devem ser armazenados e escritos como 21 :)

Cem Kalyoncu
fonte
1
Isso apenas prova que é mais rápido / fácil trabalhar em qualquer formato nativo da CPU. Não diz nada sobre se é melhor. O mesmo vale para big endian: for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }corresponde a move.l data, numuma CPU big endian.
Martin Vilcans
@ Martin: um a menos subtração é melhor no meu livro
Cem Kalyoncu
Realmente não importa, pois o compilador desenrolará o loop de qualquer maneira. De qualquer forma, muitas CPUs têm instruções de troca de bytes para lidar com esse problema.
Martin Vilcans
eu não concordo bcoz em big endian, eu faria {num << = 8; num | = dados [i]; }, Pelo menos, isso não tem que calcular contagem de deslocamento para a esquerda usando mul
Hayri Uğur Koltuk
@ali: seu código fará a operação exata que escrevi e não funcionará no big endian.
Cem Kalyoncu 26/07
1

Eu sempre me pergunto por que alguém iria querer armazenar os bytes na ordem inversa

Número decimal são escritos big endian. Também como você escreve em inglês Você começa com o dígito mais significativo e o próximo mais significativo com o menos mais significativo. por exemplo

1234

é mil duzentos e trinta e quatro.

É assim que o big endian às vezes é chamado de ordem natural.

Em little endian, esse número seria um, vinte, trezentos e quatro mil.

No entanto, quando você executa aritmética como adição ou subtração, começa com o fim.

  1234
+ 0567
  ====

Você começa com 4 e 7, escreve o dígito mais baixo e lembre-se do transporte. Em seguida, adicione 3 e 6 etc. Para adicionar, subtrair ou comparar, é mais simples de implementar, se você já possui lógica para ler a memória em ordem, se os números forem revertidos.

Para oferecer suporte a big endian dessa maneira, você precisa de lógica para ler a memória em sentido inverso ou possui um processo RISC que opera apenas em registradores. ;)

Muito do design Intel x86 / Amd x64 é histórico.

Peter Lawrey
fonte
0

O big endian é útil para algumas operações (comparações de "bignums" de molas iguais de octetos). Little-endian para outros (adicionando dois "bignums", possivelmente). No final, depende do que o hardware da CPU foi configurado, geralmente é um ou outro (alguns chips MIPS eram, IIRC, selecionáveis ​​na inicialização para LE ou BE).

Vatine
fonte
0

Quando apenas o armazenamento e a transferência com comprimentos variáveis ​​estão envolvidos, mas não há aritmética com vários valores, o LE geralmente é mais fácil de escrever, enquanto o BE é mais fácil de ler.

Vamos dar uma conversão int-to-string (e voltar) como um exemplo específico.

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

Quando o int é convertido na string, o dígito menos significativo é mais fácil de extrair do que o dígito mais significativo. Tudo isso pode ser feito em um loop simples com uma condição final simples.

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

Agora tente o mesmo na ordem BE. Normalmente, você precisa de outro divisor que detenha a maior potência de 10 para o número específico (aqui 100). Você primeiro precisa encontrar isso, é claro. Muito mais coisas para fazer.

A conversão de string para int é mais fácil de fazer no BE, quando é feita como a operação de gravação reversa. Write armazena o dígito mais significativo por último, portanto, ele deve ser lido primeiro.

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');
}

Agora faça o mesmo na ordem LE. Novamente, você precisaria de um fator adicional, começando com 1 e multiplicado por 10 para cada dígito.

Portanto, eu geralmente prefiro usar o BE para armazenamento, porque um valor é escrito exatamente uma vez, mas lido pelo menos uma vez e talvez muitas vezes. Por sua estrutura mais simples, eu também costumo seguir a rota para converter para LE e reverter o resultado, mesmo que ele grave o valor uma segunda vez.

Outro exemplo de armazenamento BE seria a codificação UTF-8 e muito mais.

Seguro
fonte