endianita, big endian se čte „big endyjen“TakéBig endian, Little endian, Byte order, Pořadí bajtů, Network byte orderPokročilý

Definice

Endianita je pravidlo, v jakém pořadí se bajty vícebajtového čísla ukládají do paměti nebo posílají po síti. Big endian zapisuje nejvýznamnější bajt jako první, little endian naopak nejméně významný. Rozdíl se projeví vždy, když jeden systém čte binární data zapsaná jiným systémem s opačnou konvencí.

Kategorie: Počítačová architekturaAktualizováno

Odkud se bere rozdíl v pořadí bajtů

Číslo 0x12345678 zabírá čtyři bajty, ale paměť je jednorozměrná posloupnost adres. Někdo se musel rozhodnout, který bajt půjde na nižší adresu. Big endian ukládá 12 34 56 78, tedy nejvýznamnější bajt první, stejně jako se u nás píší čísla. Little endian ukládá 78 56 34 12, tedy od nejnižšího řádu. Obě varianty jsou vnitřně konzistentní a žádná není objektivně lepší; problém vzniká teprve na hranici mezi systémy.

Kdo používá co

Architektury x86 a x86-64 jsou little endian, stejně jako drtivá většina dnešních ARM procesorů v běžné konfiguraci (ARM umí i big endian, ale prakticky se nenasazuje). Historicky byly big endian například SPARC, PowerPC nebo Motorola 68000. Síťové protokoly z rodiny TCP/IP naopak předepisují big endian, čemuž se říká network byte order: hlavičky IP, TCP i UDP mají čísla portů a délky uložené nejvýznamnějším bajtem napřed. Proto v C existují funkce htons a ntohl: na x86 prohodí bajty, na big endian stroji nedělají nic.

Kde se rozdíl projeví v praxi

Endianita trápí jen binární data, u nichž se čte víc bajtů jako jedno číslo. Textový JSON ani CSV problém nemají, protože číslo 1234 je v nich posloupnost znaků. Zradí naopak binární formáty souborů (TIFF, WAV, PNG), memory-mapped struktury, serializace přes struct.pack v Pythonu, čtení dat ze senzoru po SPI nebo I²C a binární protokoly psané „na koleno“ přes prosté zkopírování struktury do socketu.

Bity uvnitř bajtu

Endianita se týká pořadí bajtů, ne pořadí bitů uvnitř bajtu. To si drží hardware sám a programátor s ním v běžném kódu nepřijde do styku. Výjimkou jsou bitová pole v C, jejichž rozložení překladač volí implementačně závisle, takže je nelze bezpečně mapovat na formát na drátě.

Middle endian a BOM

Kromě dvou hlavních variant existovaly i smíšené formy (PDP-11 ukládal 32bitové hodnoty jako dvojice 16bitových slov v opačném pořadí). V Unicode řeší nejednoznačnost UTF-16 a UTF-32 značka BOM na začátku souboru: bajty FE FF znamenají big endian, FF FE little endian.

Jak se rozdílu zbavit

Spolehlivé řešení je nikdy nespoléhat na nativní rozložení CPU. Buď se použije formát s pevně danou endianitou a explicitní konverze (htonl, struct.pack('>I', x), DataView v JavaScriptu), nebo hotová serializace typu Protocol Buffers, CBOR či Avro, která pořadí bajtů definuje ve specifikaci. Formáty jako TIFF jdou třetí cestou: do hlavičky zapíší, v jaké endianitě jsou uložené, a čtečka se přizpůsobí. Nejhorší je varianta „u nás to funguje“, kdy se struktura z paměti pošle rovnou po síti a kód se rozpadne při prvním nasazení na jiné architektuře.

Příklady z praxe

  1. Stejný soubor, dvě čísla

    Program zapíše 32bitové číslo 1 přímo z paměti do souboru. Na x86 vznikne posloupnost 01 00 00 00, na big endian stroji 00 00 00 01. Když soubor přečte druhá strana bez konverze, dostane místo jedničky hodnotu 16777216. Chyba se neprojeví pádem, jen nesmyslným výsledkem.

    import struct
    struct.pack('<I', 1)   # b'\x01\x00\x00\x00'  little endian
    struct.pack('>I', 1)   # b'\x00\x00\x00\x01'  big endian
    struct.pack('=I', 1)   # nativní pořadí, závisí na stroji
  2. Detekce endianity a čtení binární hlavičky v prohlížeči

    Při zpracování nahraného binárního souboru v JavaScriptu se používá DataView, kde se endianita zadává parametrem u každého čtení. Typed arrays naopak berou nativní pořadí platformy, takže se pro parsování formátů nehodí. Test níže odhalí, jakou endianitu má stroj, na kterém kód běží.

    const buf = new ArrayBuffer(4);
    new Uint32Array(buf)[0] = 0x12345678;
    const little = new Uint8Array(buf)[0] === 0x78;
    
    const view = new DataView(buf);
    view.getUint32(0, false); // false = big endian (network order)

Časté omyly

MýtusLittle endian je pozpátku, takže je to špatně navržené.
Ve skutečnostiLittle endian má praktickou výhodu: nižší bajt leží na nejnižší adrese, takže zkrácení 32bitové hodnoty na 16bitovou nevyžaduje posun ukazatele a přičítání s přenosem jde přirozeně od začátku. Big endian zase odpovídá lidskému zápisu čísel a usnadňuje ladění hexdumpu.
MýtusDnes je všechno little endian, endianitu tedy řešit nemusím.
Ve skutečnostiEndianita zůstává živá všude, kde se míchají světy: síťové protokoly TCP/IP předepisují big endian, řada binárních formátů souborů a firmwarů mikrokontrolérů také. Kód, který pošle nativní strukturu po síti, funguje jen do chvíle, než ho někdo spustí jinde.
MýtusEndianita ovlivňuje i pořadí bitů v bajtu.
Ve skutečnostiEndianita popisuje pořadí bajtů ve víceбajtové hodnotě. Bity uvnitř bajtu si adresuje hardware a v běžném kódu je nelze prohodit; jediné místo, kde se rozložení projeví, jsou implementačně závislá bitová pole v C.

Časté dotazy

Jak zjistím, jakou endianitu má můj systém?
Endianitu systému lze zjistit zápisem známé hodnoty do víceбajtové proměnné a přečtením jejího prvního bajtu. V C se použije union int a pole char, v Pythonu stačí sys.byteorder, které vrátí řetězec 'little' nebo 'big'. V JavaScriptu se hodnota zapíše do Uint32Array a přečte přes Uint8Array. Na Linuxu poradí i příkaz lscpu, který vypisuje řádek Byte Order. Ve většině případů ale test není potřeba: lepší je psát kód, který nativní pořadí vůbec nepoužívá a všude uvádí endianitu explicitně.
Proč se síťovým protokolům předepisuje big endian?
Big endian se v síťových protokolech ustálil jako network byte order v době, kdy vznikalo TCP/IP a řada tehdejších strojů byla big endian. Zásadní je, že jde o dohodu: aby dvě libovolné implementace rozuměly hlavičkám IP, TCP nebo UDP, musí pořadí bajtů určovat specifikace, ne architektura odesílatele. Volba padla na big endian a už se nemění, protože kompatibilita by stála víc než jakákoli mikrooptimalizace. Aplikace ji řeší funkcemi htons a htonl.
Musím endianitu řešit při posílání JSON nebo XML?
Endianita se u JSON a XML neřeší, protože obě data kódují jako text. Číslo 1234 je v JSONu posloupnost čtyř znaků, ne čtyřbajtová binární hodnota, takže pořadí bajtů nehraje roli. Pozor je potřeba dát jen u kódování: UTF-16 a UTF-32 endianitu mají a řeší ji značkou BOM na začátku dokumentu. Používá-li se UTF-8, což je dnes výchozí volba, problém odpadá úplně, protože UTF-8 je posloupnost jednotlivých bajtů.
Odkud pochází název big endian a little endian?
Název big endian a little endian pochází ze satiry Gulliverovy cesty od Jonathana Swifta, kde se dva národy vedou válku o to, zda se vejce má rozbíjet ze širšího, nebo z užšího konce. Do informatiky termín zavedl Danny Cohen v poznámce o pořadí bajtů v síťových protokolech z roku 1980. Analogie je záměrně ironická: spor je z technického hlediska z velké části arbitrární, ale nedohodnout se na jedné konvenci znamená, že si systémy vzájemně nerozumí.
Jaký má endianita dopad na výkon?
Endianita sama o sobě výkon prakticky neovlivňuje, protože procesor pracuje se svým nativním pořadím bez jakékoli režie. Náklady vznikají až při konverzi, kdy se bajty prohazují: moderní procesory na to mají jedinou instrukci (BSWAP na x86, REV na ARM), takže cena je zanedbatelná i v horkých smyčkách. Znatelný dopad se objeví jen při parsování velkých objemů binárních dat po jednotlivých položkách, kde ale bývá úzkým hrdlem spíš přístup do paměti než samotné přehazování bajtů.

Zdroje

  1. Endianness(otevře se v novém okně)Wikipedia
  2. struct: Interpret bytes as packed binary data(otevře se v novém okně)Python Software Foundation
  3. DataView(otevře se v novém okně)MDN Web Docs
  4. Internet Protocol(otevře se v novém okně)RFC Editor, 1981
  5. The Unicode Standard(otevře se v novém okně)Unicode Consortium

Související pojmy

Potřebujete to vyřešit v praxi?

Poradíme, jak na to ve vašem projektu

Vysvětlit pojem je jedna věc, navrhnout kolem něj funkční řešení druhá. Ozvěte se a probereme, co dává smysl u vás.