Jaka jest zaleta małego formatu Endian?


140

Procesory Intel (i może niektóre inne) używają małego formatu Endian do przechowywania.

Zawsze zastanawiam się, dlaczego ktoś chciałby przechowywać bajty w odwrotnej kolejności. Czy ten format ma jakieś zalety w porównaniu z formatem big endian?


1
6502 był wczesnym (pierwszym?) Procesorem potokowym. Wydaje mi się, że pamiętam pewne twierdzenia, że ​​jest to mało endian z powodu problemów związanych z wydajnością z powodu potoku - ale nie mam teraz pojęcia, jaki mógł być ten problem. Jakieś sugestie?
Steve314,

1
@ Steve314: Moja odpowiedź wyjaśnia, jak mało Endian pomaga w wydajności w potokowym procesorze: programmers.stackexchange.com/q/95854/27874
Martin Vilcans

3
Little-endian, big-endian - musisz wybrać jeden lub drugi. Jak jazda po lewej lub prawej stronie drogi.

3
Sugeruję napisanie kodu w ASM, najlepiej dla architektury „oldschoolowej”, takiej jak 6502 lub Z80. Od razu zobaczysz, dlaczego używają one małego endiana. Architektury, które używają big endian, mają pewne cechy w zestawie instrukcji, co sprawia, że ​​preferowany jest ten format. Podjęcie decyzji nie jest arbitralne!
Stefan Paul Noack

2
Każdy system kolejności bajtów ma swoje zalety. Maszyny Little-endian pozwalają najpierw przeczytać najniższy bajt, bez czytania pozostałych. Możesz bardzo łatwo sprawdzić, czy liczba jest nieparzysta, czy parzysta (ostatni bit to 0), co jest fajne, jeśli lubisz takie rzeczy. Systemy Big-Endian przechowują dane w pamięci w taki sam sposób, w jaki my ludzie myślą o danych (od lewej do prawej), co ułatwia debugowanie na niskim poziomie.
Koray Tugay

Odpowiedzi:


198

W każdym razie istnieją argumenty, ale jedna kwestia jest taka, że ​​w systemie little-endian adres danej wartości w pamięci, przyjmowany jako szerokość 32, 16 lub 8 bitów, jest taki sam.

Innymi słowy, jeśli masz w pamięci dwubajtową wartość:

0x00f0   16
0x00f1    0

przyjmowanie tej „16” jako wartości 16-bitowej (c „krótkiej” w większości systemów 32-bitowych) lub jako wartości 8-bitowej (zazwyczaj c „char”) zmienia tylko instrukcję pobierania, której używasz - a nie adres, który pobierasz od.

W systemie big-endian, z powyższym przedstawionym jako:

0x00f0    0
0x00f1   16

musisz zwiększyć wskaźnik, a następnie wykonać węższą operację pobierania nowej wartości.

Krótko mówiąc: „na małych systemach endianowych obsady nie są możliwe”.


3
Zakładając oczywiście, że bajty wysokiego rzędu, których nie przeczytałeś, można rozsądnie zignorować (np. Wiesz, że i tak są zerowe).
Steve314,

10
@ Steve314: Jeśli jestem w C-downcastingu z 32 do 16 bitów (np.) W systemie z uzupełnieniami do 2 - ogromna większość systemów - bajty nie muszą być zerowe, aby zostać zignorowane. Bez względu na ich wartość mogę je zignorować i pozostać zgodnym ze standardem C i oczekiwaniami programisty.

9
@ Stritzinger - mówimy o kodzie asemblera / maszynowym generowanym przez kompilator, który nie może być przenośny. Kod języka wyższego poziomu do kompilacji jest przenośny - po prostu kompiluje się do różnych operacji na różnych architekturach (jak robią to wszyscy operatorzy).
jimwise

7
Nie kupuję tego argumentu, ponieważ w architekturze big-endian wskaźnik może wskazywać na koniec, a nie na początek czegoś, do czego się odwołujesz, i miałbyś dokładnie taką samą przewagę.
dan_waterworth

4
@dan_waterworth niezupełnie - pamiętaj na przykład o regułach arytmetycznych wskaźnika w C i o tym, co się stanie, gdy zwiększysz lub zmniejszysz rzutów tego samego wskaźnika. Możesz przenieść złożoność, ale nie możesz jej wyeliminować.
jimwise

45

Zawsze zastanawiam się, dlaczego ktoś chciałby przechowywać bajty w odwrotnej kolejności.

Big-endian i little-endian są tylko „normalnym porządkiem” i „odwrotnym porządkiem” z ludzkiej perspektywy, i tylko wtedy, gdy wszystkie z nich są prawdziwe ...

  1. Czytasz wartości na ekranie lub na papierze.
  2. Umieszczasz niższe adresy pamięci po lewej, a wyższe adresy po prawej.
  3. Piszesz szesnastkowo, z wysokim rzędem po lewej stronie lub dwójkowym, z najbardziej znaczącym bitem po lewej.
  4. Czytasz od lewej do prawej.

To wszystkie ludzkie konwencje, które nie mają żadnego znaczenia dla procesora. Jeśli zachowasz numer 1 i 2, a następnie odwrócisz 3, little-endian wydawałby się „całkowicie naturalny” dla osób czytających arabski lub hebrajski, które są pisane od prawej do lewej.

Są też inne ludzkie konwencje, które sprawiają, że big-endian wydaje się nienaturalny, jak ...

  • „Wyższy” (najbardziej znaczący) bajt powinien znajdować się na „wyższym” adresie pamięci.

Kiedy programowałem głównie 68K i PowerPC, uważałem big-endian za „właściwy”, a little-endian za „zły”. Ale odkąd wykonuję więcej ARM i Intela, przyzwyczaiłem się do little-endian. To naprawdę nie ma znaczenia.


30
Liczby są w rzeczywistości zapisywane od [najbardziej znaczącej cyfry] od lewej do [najmniej znaczącej cyfry] w prawo w języku arabskim i hebrajskim.
Random832

5
Dlaczego więc bity w bajcie są przechowywane w formacie „big endian”? Dlaczego nie być konsekwentnym?
tskuzzy

11
Nie są - bit 0 jest z reguły najmniej znaczący, a bit 7 jest najbardziej znaczący. Co więcej, generalnie nie można porządkować bitów w bajcie, ponieważ bitów nie można indywidualnie adresować. Oczywiście mogą mieć fizyczny porządek w danym protokole komunikacyjnym lub na nośniku pamięci, ale jeśli nie pracujesz na niskim poziomie protokołu lub na poziomie sprzętowym, nie musisz się martwić o to zamówienie.
Stewart

3
BlueRaja: tylko zgodnie z konwencją pisania na papierze. Nie ma to nic wspólnego z architekturą procesora. Możesz zapisać bajt jako 0-7 LSB-MSB zamiast 7-0 MSB-LSB i nic nie zmienia się z punktu widzenia algorytmu.
SF.

2
@SF .: „Wciśnij krótko, pop wszystko, ale nie krótko ” i tak cię zaskoczy. Nawet jeśli nie psujesz stosu przez pchanie bajtów, których nigdy nie pop lub odwrotnie ... x86 (32-bit), na przykład, naprawdę naprawdę chce, aby stos był wyrównany do dworda, i pchanie lub popping wszystkiego, co powoduje wskaźnik stosu nie będący wielokrotnością 4 może powodować problemy z wyrównaniem. I nawet jeśli tak się nie stanie, rzeczy pchają jednocześnie całe słowo / dword / qword / etc - tak więc niski bajt nadal będzie pierwszym, który dostaniesz, kiedy wyskoczysz.
cHao

41

OK, oto powód, dla którego mi to wyjaśniło: dodawanie i odejmowanie

Kiedy dodajesz lub odejmujesz liczby wielobajtowe, musisz zacząć od najmniej znaczącego bajtu. Jeśli dodajesz na przykład dwie liczby 16-bitowe, może istnieć przeniesienie z najmniej znaczącego bajtu na najbardziej znaczący, więc musisz zacząć od najmniej znaczącego bajtu, aby sprawdzić, czy istnieje przeniesienie. Jest to ten sam powód, dla którego zaczynasz od prawej cyfry podczas dodawania długiego. Nie możesz zacząć od lewej.

Rozważ 8-bitowy system, który pobiera bajty sekwencyjnie z pamięci. Jeśli najpierw pobiera najmniej znaczący bajt , może rozpocząć dodawanie, podczas gdy najbardziej znaczący bajt jest pobierany z pamięci. Ten paralelizm jest powodem, dla którego wydajność jest lepsza w małych systemach endianowych, takich jak system. Gdyby musiał czekać na pobranie obu bajtów z pamięci lub pobrać je w odwrotnej kolejności, zajęłoby to więcej czasu.

Dzieje się tak na starych systemach 8-bitowych. W nowoczesnym procesorze wątpię, aby kolejność bajtów miała jakąkolwiek różnicę i używamy little endian tylko z powodów historycznych.


3
Ach - więc jest to mniej więcej ten sam powód, dla którego używam sortowania dla małych liczb całkowitych dla dużych liczb całkowitych. Powinienem to wymyślić. Ludzie naprawdę muszą teraz pracować nad cybernetyką - mój mózg już desperacko potrzebuje części zamiennych i radykalnych ulepszeń, nie mogę czekać wiecznie!
Steve314,

2
Myśl - 6502 nie zrobiła wiele 16-bitowej matematyki w sprzęcie - w końcu był to 8-bitowy procesor. Ale robiło to adresowanie względne, używając 8-bitowych przesunięć względem 16-bitowego adresu bazowego.
Steve314,

2
Zauważ, że ten pomysł wciąż ma znaczenie dla arytmetyki liczb całkowitych wielokrotnych precyzji (jak powiedział Steve314), ale na poziomie słowa. Obecnie na endianność procesora nie ma bezpośredniego wpływu na większość operacji: nadal można zapisać najmniej znaczące słowo w systemie big-endian, tak jak robi to GMP. Procesory Little-endian wciąż mają przewagę nad kilkoma operacjami (np. Niektóre konwersje łańcuchów?), Które można łatwiej wykonać, czytając jeden bajt na raz, ponieważ tylko w systemie little-endian porządkowanie bajtów takich liczb jest poprawne.
vinc17

Procesory little-endian mają tę zaletę, że przepustowość pamięci jest ograniczona, jak w niektórych 32-bitowych procesorach ARM z 16-bitową szyną pamięci lub w 8088 z 8-bitową szyną danych: procesor może po prostu załadować niską połowę i wykonać dodaj / sub / mul ... z nim, czekając na wyższą połowę
phuclv

13

Dzięki 8-bitowym procesorom było to z pewnością bardziej wydajne, można było wykonać 8 lub 16-bitową operację bez potrzeby używania innego kodu i bez buforowania dodatkowych wartości.

Nadal lepiej jest w przypadku niektórych operacji dodawania, jeśli masz do czynienia z bajtem naraz.

Ale nie ma powodu, dla którego big-endian jest bardziej naturalny - w języku angielskim używasz trzynastu (mały endian) i dwudziestu trzech (duży endian)


1
Big-endian jest rzeczywiście łatwiejszy dla ludzi, ponieważ nie wymaga zmiany kolejności bajtów. Na przykład na PC 0x12345678jest przechowywany jako, 78 56 34 12podczas gdy w systemie BE jest 12 34 56 78(bajt 0 jest po lewej stronie, bajt 3 jest po prawej). Zauważ, że im większa jest liczba (pod względem bitów), tym bardziej wymaga wymiany; SŁOWO wymagałoby jednej zamiany; DWORD, dwa przejścia (trzy całkowite zamiany); QWORD trzy przejścia (łącznie 7) i tak dalej. To znaczy (bits/8)-1swapy. Inną opcją jest czytanie ich zarówno do przodu, jak i do tyłu (czytanie każdego bajtu do przodu, ale skanowanie całego # do tyłu).
Synetech

Sto trzynaście to albo środkowy endian, albo też big-endian, przy czym „trzynaście” jest zasadniczo jedną cyfrą dziesiętną. Kiedy przeliterujemy liczby, istnieją pewne niewielkie odstępstwa od konwencji stałej bazy, których używamy do cyfr, ale kiedy
usuniesz

@ Synetech - na szczęście komputer nie musi dbać o to, jak ludzie je czytają. To tak, jakby twierdzić, że flash NAND jest lepszy, ponieważ ot '
Martin Beckett

1
@ Steve314, przeliterowane słowa liczb nie mają znaczenia, to jest odczyt numeryczny, którego używamy podczas programowania. Martin, żaden komputer nie musi dbać o to, jak ludzie odczytują liczby, ale jeśli ludzie łatwo je odczytają, programowanie (lub inne powiązane prace) stają się łatwiejsze, a niektóre wady i błędy można zmniejszyć lub uniknąć.
Synetech

@ steve314 A w języku duńskim „95” jest wymawiane jako „fem halvfems” (pięć plus cztery i pół dwudziestki).
Vatine

7

Japońska konwencja dat to „big endian” - rrrr / mm / dd. Jest to przydatne w przypadku algorytmów sortowania, w których można użyć prostego porównania ciągów ze zwykłą regułą pierwszego znaku, która jest najbardziej znacząca.

Coś podobnego dotyczy liczb big-endian przechowywanych w rekordzie o największym znaczeniu na polu. Kolejność ważności bajtów w polach odpowiada znaczeniu pól w rekordzie, więc możesz użyć a memcmpdo porównania rekordów, nie dbając o to, czy porównujesz dwa długie słowa, cztery słowa, czy osiem osobnych bajtów.

Odwróć kolejność znaczenia pól, aby uzyskać tę samą przewagę, ale w przypadku liczb małych endianów zamiast big-endianów.

Ma to oczywiście bardzo niewielkie znaczenie praktyczne. Niezależnie od tego, czy twoja platforma to big-endian czy little-endian, możesz zamówić pola rekordów, aby wykorzystać tę sztuczkę, jeśli naprawdę tego potrzebujesz. To tylko ból, jeśli musisz napisać przenośny kod.

Równie dobrze mogę dołączyć link do klasycznego odwołania ...

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

EDYTOWAĆ

Dodatkowa myśl. Kiedyś napisałem dużą bibliotekę liczb całkowitych (aby zobaczyć, czy mógłbym), a do tego fragmenty o szerokości 32 bitów są przechowywane w kolejności little-endian, niezależnie od tego, jak platforma porządkuje bity w tych fragmentach. Powody były ...

  1. Wiele algorytmów po prostu naturalnie zaczyna działać na najmniej znaczącym końcu i chce, aby te końce były dopasowane. Na przykład dodatkowo przenosi propagację do coraz bardziej znaczących cyfr, więc sensowne jest rozpoczęcie od co najmniej znaczącego końca.

  2. Zwiększenie lub zmniejszenie wartości oznacza po prostu dodanie / usunięcie fragmentów na końcu - nie trzeba przesuwać fragmentów w górę / w dół. Kopiowanie może być nadal potrzebne ze względu na realokację pamięci, ale nie często.

Nie ma to oczywiście oczywistego znaczenia dla procesorów - dopóki procesory nie zostaną wyposażone w sprzętową obsługę dużych liczb całkowitych, jest to czysto biblioteka.


7

Nikt inny nie odpowiedział DLACZEGO można to zrobić, wiele rzeczy na temat konsekwencji.

Rozważ 8-bitowy procesor, który może załadować jeden bajt z pamięci w danym cyklu zegara.

Teraz, jeśli chcesz załadować 16-bitową wartość, do (powiedzmy) jedynego 16-bitowego rejestru, który masz - tj. Licznika programu, to prosty sposób to zrobić:

  • Załaduj bajt z lokalizacji pobierania
  • przesuń ten bajt w lewo o 8 miejsc
  • Zwiększ lokalizację pobierania pamięci o 1
  • wczytaj następny bajt (do niskiej części rejestru)

wynik: zawsze zwiększasz tylko lokalizację pobierania, zawsze ładujesz tylko do niższej części szerszego rejestru i wystarczy, że możesz przesunąć w lewo. (Oczywiście przesunięcie w prawo jest pomocne w przypadku innych operacji, więc jest to trochę side show).

Konsekwencją tego jest to, że 16-bitowe (dwubajtowe) elementy są przechowywane w kolejności Most..Last. To znaczy, mniejszy adres ma najbardziej znaczący bajt - tak duży endian.

Jeśli zamiast tego próbowałeś załadować przy użyciu małego endiana, musisz załadować bajt do dolnej części szerokiego rejestru, a następnie załadować następny bajt do obszaru pomostowego, przesunąć go, a następnie wrzucić do górnej części szerszego rejestru . Lub użyj bardziej złożonego układu bramkowania, aby móc selektywnie ładować do górnego lub dolnego bajtu.

Rezultatem próby przejścia na mały endian jest to, że albo potrzebujesz więcej krzemu (przełączniki i bramki), albo więcej operacji.

Innymi słowy, jeśli chodzi o odzyskanie huku za dawne czasy, masz większy huk dla większości wydajności i najmniejszego obszaru krzemowego.

Obecnie te rozważania są praktycznie nieistotne, ale takie rzeczy, jak wypełnianie rurociągów, mogą nadal stanowić poważny problem.

Jeśli chodzi o pisanie s / w, życie jest często łatwiejsze, gdy używa się małego adresowania endian.

(A duże procesory endian są zwykle dużymi endianami pod względem kolejności bajtów i małym endianem pod względem bitów w bajtach. Ale niektóre procesory są dziwne i będą używać kolejności bitów big endian, a także kolejności bajtów. To czyni życie bardzo ciekawe dla projektanta sprzętowego dodającego urządzenia peryferyjne odwzorowane w pamięci, ale nie ma innych konsekwencji dla programisty.)


3

Jimwise miał rację. Jest jeszcze jeden problem, w little endian możesz wykonać następujące czynności:

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

Bardziej prosto dla programistów, którzy nie odczuwają oczywistej wady zamienionych lokalizacji w pamięci. Osobiście uważam, że duży endian jest odwrotnością tego, co naturalne :). 12 należy zapisać i zapisać jako 21 :)


1
To tylko dowodzi, że szybsza / łatwiejsza praca w dowolnym formacie natywnym dla procesora. Nie mówi nic o tym, czy jest lepiej. To samo dotyczy dużego endianu: for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }odpowiada move.l data, numprocesorowi z dużym endianem.
Martin Vilcans,

@martin: w mojej książce lepiej jest odjąć mniej odejmowania
Cem Kalyoncu,

To naprawdę nie ma znaczenia, ponieważ kompilator i tak rozwinie pętlę. W każdym razie wiele procesorów ma instrukcje zamiany bajtów, aby poradzić sobie z tym problemem.
Martin Vilcans,

nie zgadzam się z BCoz na Big Endian, zrobiłbym {num << = 8; num | = dane [i]; } przynajmniej to nie musi obliczać liczby przesunięć w lewo za pomocą
mula

@ali: twój kod wykona dokładnie tę operację, którą napisałem i nie będzie działał na big endian.
Cem Kalyoncu

1

Zawsze zastanawiam się, dlaczego ktoś chciałby przechowywać bajty w odwrotnej kolejności

Liczby dziesiętne są zapisywane jako duży endian. To także sposób pisania po angielsku Zaczynasz od najbardziej znaczącej cyfry, a następnie od największej do najmniej znaczącej. na przykład

1234

jest tysiąc dwieście trzydzieści cztery.

W ten sposób wielki endian nazywany jest czasem porządkiem naturalnym.

W małym endianie liczba ta wynosiłaby jeden, dwadzieścia, trzysta cztery tysiące.

Kiedy jednak wykonujesz arytmetykę, taką jak dodawanie lub odejmowanie, zaczynasz od końca.

  1234
+ 0567
  ====

Zaczynasz od 4 i 7, pisz najniższą cyfrę i zapamiętaj przeniesienie. Następnie dodajesz 3 i 6 itd. Aby dodać, odjąć lub porównać, łatwiej jest zaimplementować, jeśli masz już logikę do odczytu pamięci w kolejności, jeśli liczby są odwrócone.

Aby w ten sposób wspierać Big Endian, potrzebujesz logiki do odczytu pamięci w odwrotnej kolejności lub masz proces RISC, który działa tylko na rejestrach. ;)

Wiele konstrukcji procesorów Intel x86 / Amd x64 jest historycznych.


0

Big-endian jest przydatny w niektórych operacjach (w porównaniu do „bignum” równych sprężyn o długości oktetu). Little-endian dla innych (możliwe dodanie dwóch „bignum”). Ostatecznie zależy to od tego, do czego sprzęt CPU został skonfigurowany, zwykle jest to jeden lub drugi (niektóre układy MIPS były, IIRC, przełączalne przy rozruchu na LE lub BE).


0

Gdy w grę wchodzą tylko przechowywanie i przesyłanie o różnych długościach, ale brak arytmetyki z wieloma wartościami, LE jest zwykle łatwiejsze do zapisu, a BE łatwiejsze do odczytania.

Weźmy konwersję int-na-string (i wstecz) jako konkretny przykład.

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

Kiedy int jest konwertowany na ciąg, to cyfra najmniej znacząca jest łatwiejsza do wyodrębnienia niż cyfra najbardziej znacząca. Wszystko to można zrobić w prostej pętli z prostym warunkiem końcowym.

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

Teraz spróbuj tego samego w kolejności BE. Zwykle potrzebujesz innego dzielnika, który ma największą moc 10 dla określonej liczby (tutaj 100). Oczywiście najpierw musisz to znaleźć. Znacznie więcej rzeczy do zrobienia.

Konwersja ciągu na int jest łatwiejsza w BE, gdy jest wykonywana jako operacja zapisu wstecznego. Zapisuje w pamięci najbardziej znaczącą cyfrę na końcu, dlatego należy ją najpierw przeczytać.

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

Teraz zrób to samo w kolejności LE. Ponownie potrzebujesz dodatkowego współczynnika zaczynającego się od 1 i mnożonego przez 10 dla każdej cyfry.

Dlatego zazwyczaj wolę używać BE do przechowywania, ponieważ wartość jest zapisywana dokładnie raz, ale czytana przynajmniej raz, a może wiele razy. Ze względu na prostszą strukturę zwykle wybieram również trasę do konwersji na LE, a następnie odwracam wynik, nawet jeśli zapisuje wartość po raz drugi.

Innym przykładem pamięci BE byłoby kodowanie UTF-8 i wiele innych.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.