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?
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?
Odpowiedzi:
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”.
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 ...
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 ...
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.
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.
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)
0x12345678
jest przechowywany jako, 78 56 34 12
podczas 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)-1
swapy. 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).
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 memcmp
do 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 ...
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.
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.
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ć:
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.)
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 :)
for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }
odpowiada move.l data, num
procesorowi z dużym endianem.
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.
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).
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.