โปรเซสเซอร์ของ Intel (และอื่น ๆ ) อาจใช้รูปแบบ endian เล็กน้อยสำหรับการจัดเก็บ
ฉันมักจะสงสัยว่าทำไมบางคนต้องการเก็บไบต์ในลำดับที่กลับกัน รูปแบบนี้มีข้อดีมากกว่ารูปแบบ endian ใหญ่หรือไม่
โปรเซสเซอร์ของ Intel (และอื่น ๆ ) อาจใช้รูปแบบ endian เล็กน้อยสำหรับการจัดเก็บ
ฉันมักจะสงสัยว่าทำไมบางคนต้องการเก็บไบต์ในลำดับที่กลับกัน รูปแบบนี้มีข้อดีมากกว่ารูปแบบ endian ใหญ่หรือไม่
คำตอบ:
มีข้อโต้แย้งอย่างใดอย่างหนึ่ง แต่จุดหนึ่งคือในระบบเล็ก ๆ ที่อยู่ของค่าที่กำหนดในหน่วยความจำนำมาเป็นความกว้าง 32, 16, หรือ 8 บิตเหมือนกัน
กล่าวอีกนัยหนึ่งถ้าคุณมีหน่วยความจำสองค่าไบต์:
0x00f0 16
0x00f1 0
การใช้ '16' เป็นค่า 16 บิต (c 'short' ในระบบ 32 บิตส่วนใหญ่) หรือเป็นค่า 8 บิต (โดยทั่วไป c 'char') จะเปลี่ยนเฉพาะคำแนะนำการดึงข้อมูลที่คุณใช้ไม่ใช่ที่อยู่ที่คุณดึงข้อมูล จาก.
ในระบบ big-endian โดยที่ข้อความข้างต้นจัดเป็น:
0x00f0 0
0x00f1 16
คุณจะต้องเพิ่มตัวชี้แล้วดำเนินการดึงข้อมูลที่แคบลงในค่าใหม่
ดังนั้นในระยะสั้น 'ในระบบ endian เล็ก ๆ น้อย ๆ ปลดเปลื้องเป็น no-op.
ฉันมักจะสงสัยว่าทำไมบางคนต้องการเก็บไบต์ในลำดับที่กลับกัน
Big-endian และ little-endian เป็นเพียง "การสั่งซื้อปกติ" และ "การสั่งซื้อย้อนกลับ" จากมุมมองของมนุษย์และจากนั้นหากสิ่งเหล่านี้เป็นจริง ...
สิ่งเหล่านี้ล้วนเป็นแบบแผนของมนุษย์ที่ไม่สำคัญเลยสำหรับซีพียู หากคุณต้องรักษา # 1 และ # 2 และพลิก # 3 ผู้เอนกายเล็ก ๆ จะดูเหมือน "เป็นธรรมชาติอย่างสมบูรณ์" สำหรับผู้ที่อ่านภาษาอาหรับหรือฮิบรูซึ่งเขียนจากขวาไปซ้าย
และยังมีการประชุมอื่น ๆ ของมนุษย์ที่ทำให้คนหัวโตที่ดูเหมือนไม่เป็นธรรมชาติเช่น ...
ย้อนกลับไปตอนที่ฉันเขียนโปรแกรมส่วนใหญ่ 68K และ PowerPC ฉันคิดว่าบิ๊ก - เอนเดี้ยนเป็น "ถูกต้อง" และเอนด์ - เล็ก ๆ น้อย ๆ ที่จะ "ผิด" แต่เนื่องจากฉันได้ทำงานกับ ARM และ Intel มากขึ้นฉันจึงคุ้นเคยกับเด็กน้อย มันไม่สำคัญหรอก
ตกลงนี่คือเหตุผลที่ฉันอธิบายให้ฉัน: การบวกและการลบ
เมื่อคุณเพิ่มหรือลบหมายเลขหลายไบต์คุณต้องเริ่มต้นด้วยไบต์ที่มีนัยสำคัญน้อยที่สุด หากคุณกำลังเพิ่มหมายเลข 16 บิตสองตัวอาจมีการพกพาจากไบต์ที่สำคัญน้อยที่สุดไปยังไบต์ที่สำคัญที่สุดดังนั้นคุณต้องเริ่มต้นด้วยไบต์ที่มีนัยสำคัญน้อยที่สุดเพื่อดูว่ามีการพกพาหรือไม่ นี่คือเหตุผลเดียวกันกับที่คุณเริ่มต้นด้วยตัวเลขที่ถูกต้องที่สุดเมื่อทำการบวกระยะยาว คุณไม่สามารถเริ่มจากด้านซ้าย
พิจารณาระบบ 8 บิตที่ดึงข้อมูลไบต์ตามลำดับจากหน่วยความจำ หากดึงข้อมูลไบต์ที่มีความสำคัญน้อยที่สุดมาก่อนก็สามารถเริ่มทำการเพิ่มได้ในขณะที่ไบต์ที่สำคัญที่สุดกำลังถูกดึงมาจากหน่วยความจำ ความเท่าเทียมนี้คือเหตุผลที่ประสิทธิภาพดีขึ้นในระบบเล็ก ๆ น้อย ๆ หากต้องรอจนกว่าไบต์ทั้งสองจะถูกดึงออกมาจากหน่วยความจำหรือดึงข้อมูลในลำดับย้อนกลับจะใช้เวลานานขึ้น
นี่เป็นระบบ 8 บิตเก่า บน CPU ที่ทันสมัยฉันสงสัยว่าคำสั่งไบต์สร้างความแตกต่างและเราใช้ endian เพียงเล็กน้อยเพื่อเหตุผลทางประวัติศาสตร์
ด้วยตัวประมวลผล 8 บิตมันมีประสิทธิภาพมากกว่าแน่นอนคุณสามารถดำเนินการ 8 หรือ 16 บิตโดยไม่จำเป็นต้องใช้โค้ดที่แตกต่างกันและไม่จำเป็นต้องบัฟเฟอร์ค่าเพิ่มเติม
จะยังดีกว่าสำหรับการดำเนินการเพิ่มเติมหากคุณจัดการไบต์ในเวลา
แต่ไม่มีเหตุผลว่า big-endian นั้นเป็นธรรมชาติมากกว่า - ในภาษาอังกฤษคุณใช้สิบสาม (endian น้อย) และยี่สิบสาม (big endian)
0x12345678
จะถูกเก็บไว้ใน78 56 34 12
ขณะที่อยู่บนระบบ BE 12 34 56 78
(ไบต์ 0 อยู่ทางซ้ายไบต์ 3 อยู่ทางขวา) สังเกตว่าจำนวนที่มากขึ้นนั้นเป็นอย่างไร (ในรูปของบิต) ยิ่งต้องมีการแลกเปลี่ยนมากขึ้น คำจะต้องมีการแลกเปลี่ยน; a DWORD, สองครั้งที่ผ่านไป (สามการสลับรวม); QWORD สามรอบ (รวม 7 ครั้ง) และอื่น ๆ นั่นคือการ(bits/8)-1
แลกเปลี่ยน อีกทางเลือกหนึ่งคือการอ่านพวกเขาทั้งไปข้างหน้าและข้างหลัง (อ่านแต่ละไบต์ไปข้างหน้า แต่การสแกนทั้ง # ย้อนหลัง)
การประชุมวันที่ญี่ปุ่นคือ "big endian" - yyyy / mm / dd สิ่งนี้มีประโยชน์สำหรับการจัดเรียงอัลกอริทึมซึ่งสามารถใช้การเปรียบเทียบสตริงอย่างง่ายกับกฎตัวแรก - ตัว - - เป็นสิ่งที่สำคัญที่สุด
บางสิ่งที่คล้ายกันนั้นใช้กับหมายเลข big-endian ที่เก็บไว้ในระเบียนที่มีความสำคัญที่สุดในฟิลด์แรก ลำดับความสำคัญของไบต์ภายในเขตข้อมูลตรงกับความสำคัญของเขตข้อมูลภายในระเบียนดังนั้นคุณสามารถใช้ a memcmp
เพื่อเปรียบเทียบระเบียนไม่สนใจมากไม่ว่าคุณจะเปรียบเทียบ longwords สองคำสี่คำหรือแปดไบต์แยกกัน
พลิกลำดับความสำคัญของเขตข้อมูลและคุณจะได้รับประโยชน์เหมือนกัน แต่สำหรับผู้ที่มีเลขตัวเล็ก ๆ แทนที่จะเป็นคนตัวใหญ่
แน่นอนว่ามันมีความสำคัญในทางปฏิบัติน้อยมาก ไม่ว่าแพลตฟอร์มของคุณจะเป็นใหญ่หรือเล็กคุณสามารถสั่งเขตข้อมูลระเบียนเพื่อใช้ประโยชน์จากเคล็ดลับนี้ถ้าคุณต้องการ มันเป็นแค่ความเจ็บปวดหากคุณจำเป็นต้องเขียนโค้ดแบบพกพา
ฉันอาจรวมลิงก์ไปยังอุทธรณ์คลาสสิก ...
http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt
แก้ไข
ความคิดที่พิเศษ ฉันเคยเขียนไลบรารี่จำนวนเต็มขนาดใหญ่ (เพื่อดูว่าฉันสามารถทำได้) และสำหรับชิ้นนั้นขนาด 32- บิตจะถูกเก็บไว้ในลำดับเล็ก ๆ น้อย ๆ โดยไม่คำนึงถึงวิธีที่แพลตฟอร์มสั่งบิตในชิ้นเหล่านั้น เหตุผลคือ ...
อัลกอริธึมมากมายเริ่มทำงานอย่างเป็นธรรมชาติที่จุดสิ้นสุดที่สำคัญน้อยที่สุดและต้องการให้จับคู่สิ้นสุดเหล่านั้น ตัวอย่างเช่นนอกจากนี้การแพร่กระจายไปยังตัวเลขที่มีนัยสำคัญมากขึ้นดังนั้นจึงควรเริ่มต้นที่จุดสิ้นสุดที่สำคัญน้อยที่สุด
การเพิ่มหรือลดขนาดของค่าหมายถึงการเพิ่ม / ลบชิ้นส่วนในตอนท้าย - ไม่จำเป็นต้องเลื่อนชิ้นขึ้น / ลง อาจจำเป็นต้องคัดลอกเนื่องจากการจัดสรรหน่วยความจำใหม่ แต่ไม่บ่อยครั้ง
สิ่งนี้ไม่มีความเกี่ยวข้องอย่างชัดเจนกับตัวประมวลผล - จนกว่า CPU จะสร้างด้วยการรองรับจำนวนเต็มจำนวนมากของฮาร์ดแวร์มันเป็นเรื่องของห้องสมุดอย่างแท้จริง
ไม่มีใครตอบว่าทำไมจึงต้องทำสิ่งนี้มากมายเกี่ยวกับผลที่ตามมา
พิจารณาโปรเซสเซอร์ 8 บิตซึ่งสามารถโหลดไบต์เดียวจากหน่วยความจำในรอบสัญญาณนาฬิกาที่กำหนด
ตอนนี้ถ้าคุณต้องการโหลดค่า 16 บิตลงไป (พูด) การลงทะเบียนเพียงหนึ่งบิตและ 16 บิตที่คุณมี - เช่นตัวนับโปรแกรมจากนั้นวิธีง่ายๆในการทำคือ:
ผลลัพธ์: คุณเพิ่มตำแหน่งการดึงเท่านั้นคุณจะโหลดเฉพาะส่วนที่มีลำดับต่ำของการลงทะเบียนที่กว้างขึ้นและคุณจะต้องเลื่อนไปทางซ้ายเท่านั้น (แน่นอนการขยับขวาจะมีประโยชน์สำหรับการดำเนินการอื่นดังนั้นอันนี้เป็นการแสดงด้านข้างเล็กน้อย)
ผลที่ตามมาก็คือสิ่งที่ 16 บิต (ไบต์คู่) จะถูกเก็บไว้ในคำสั่งมากที่สุด ..Least นั่นคือที่อยู่ที่เล็กกว่ามีไบต์ที่สำคัญที่สุด - endian ใหญ่มาก
หากคุณพยายามโหลดโดยใช้ endian น้อยคุณจะต้องโหลดไบต์ลงในส่วนล่างของการลงทะเบียนแบบกว้างของคุณจากนั้นโหลดไบต์ถัดไปลงในพื้นที่การแสดงละครเลื่อนจากนั้นนำมาไว้ด้านบนของการลงทะเบียนที่กว้างขึ้น . หรือใช้การจัดเรียงที่ซับซ้อนมากขึ้นของ gating เพื่อให้สามารถเลือกโหลดลงในไบต์ด้านบนหรือด้านล่าง
ผลลัพธ์ของการพยายามไปสู่ endian เล็ก ๆ น้อย ๆ ก็คือคุณต้องการซิลิคอนมากขึ้น (สวิตช์และประตู) หรือการทำงานที่มากขึ้น
กล่าวอีกนัยหนึ่งในแง่ของการได้รับผลตอบแทนที่มากในสมัยก่อนคุณได้รับผลตอบแทนที่มากขึ้นสำหรับพื้นที่ส่วนใหญ่และซิลิคอนที่เล็กที่สุด
วันนี้การพิจารณาเหล่านี้และไม่เกี่ยวข้องมากสวย แต่สิ่งต่าง ๆ เช่นการเติมไปป์ไลน์อาจยังคงเป็นเรื่องใหญ่
เมื่อพูดถึงการเขียน s / w ชีวิตมักจะง่ายขึ้นเมื่อใช้การจัดการกับ endian เล็กน้อย
(และโปรเซสเซอร์ endian ใหญ่มีแนวโน้มที่จะ endian ใหญ่ในแง่ของการสั่งซื้อไบต์และ endian เล็ก ๆ น้อย ๆ ในแง่ของบิตในไบต์. แต่การประมวลผลบางอย่างที่แปลกและจะใช้บิต endian ใหญ่การสั่งซื้อเช่นเดียวกับการสั่งซื้อไบต์. นี้ทำให้ชีวิตมากน่าสนใจสำหรับนักออกแบบ h / w ที่เพิ่มอุปกรณ์ต่อพ่วงหน่วยความจำที่แมป แต่ไม่มีผลอื่นใดเกิดขึ้นกับโปรแกรมเมอร์)
jimwise ทำให้เป็นจุดที่ดี มีปัญหาอื่นใน endian น้อยคุณสามารถทำสิ่งต่อไปนี้:
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
ตรงไปข้างหน้ามากขึ้นสำหรับโปรแกรมเมอร์ที่ไม่ได้รับผลกระทบจากข้อเสียที่เห็นได้ชัดจากการเปลี่ยนตำแหน่งในหน่วยความจำ โดยส่วนตัวแล้วฉันพบว่า endian ใหญ่ ๆ จะกลับกันในสิ่งที่เป็นธรรมชาติ :) 12 ควรเก็บและเขียนเป็น 21 :)
for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }
สอดคล้องกับmove.l data, num
CPU ตัวใหญ่ของ endian
ฉันมักจะสงสัยว่าทำไมบางคนต้องการเก็บไบต์ในลำดับที่กลับกัน
เลขทศนิยมเขียนเป็น endian ใหญ่ นอกจากนี้วิธีที่คุณเขียนเป็นภาษาอังกฤษคุณเริ่มต้นด้วยตัวเลขที่สำคัญที่สุดและสำคัญที่สุดถัดไปเป็นสำคัญที่สุด เช่น
1234
คือหนึ่งพันสองร้อยสามสิบสี่
นี่เป็นวิธีที่ endian ใหญ่บางครั้งเรียกว่าระเบียบตามธรรมชาติ
ใน endian น้อยตัวเลขนี้จะเป็นหนึ่งสองหมื่นสามร้อยสี่พัน
อย่างไรก็ตามเมื่อคุณดำเนินการทางคณิตศาสตร์เช่นการบวกหรือการลบคุณจะเริ่มต้นด้วยการสิ้นสุด
1234
+ 0567
====
คุณเริ่มต้นด้วย 4 และ 7 เขียนตัวเลขต่ำสุดและจำการพกพา จากนั้นคุณเพิ่ม 3 และ 6 เป็นต้นสำหรับการบวกลบหรือเปรียบเทียบมันง่ายกว่าที่จะนำมาใช้ถ้าคุณมีตรรกะในการอ่านหน่วยความจำตามลำดับถ้าตัวเลขกลับด้าน
เพื่อรองรับ endian ขนาดใหญ่ด้วยวิธีนี้คุณต้องใช้ตรรกะในการอ่านหน่วยความจำแบบย้อนกลับหรือคุณมีกระบวนการ RISC ซึ่งทำงานกับการลงทะเบียนเท่านั้น ;)
การออกแบบ Intel x86 / Amd x64 จำนวนมากเป็นเรื่องในอดีต
Big-endian มีประโยชน์สำหรับการดำเนินการบางอย่าง (การเปรียบเทียบ "bignums" ของน้ำพุความยาว octet เท่ากันกับใจ) Little-endian สำหรับผู้อื่น (อาจเพิ่ม "bignums" สองรายการ) ในท้ายที่สุดมันขึ้นอยู่กับสิ่งที่ฮาร์ดแวร์ CPU ได้รับการตั้งค่าโดยปกติจะเป็นหนึ่งหรืออื่น ๆ (ชิป MIPS บางตัวเป็น IIRC สามารถสลับได้ในการบูตเป็น LE หรือ BE)
เมื่อมีการจัดเก็บและถ่ายโอนที่มีความยาวผันแปรเท่านั้นที่เกี่ยวข้อง แต่ไม่มีเลขคณิตที่มีหลายค่า LE มักเขียนได้ง่ายกว่าในขณะที่ BE อ่านได้ง่ายกว่า
ลองทำการแปลง int-to-string (และย้อนกลับ) เป็นตัวอย่างที่เฉพาะเจาะจง
int val_int = 841;
char val_str[] = "841";
เมื่อ int ถูกแปลงเป็นสตริงตัวเลขที่มีนัยสำคัญน้อยที่สุดนั้นจะแยกได้ง่ายกว่าตัวเลขที่สำคัญที่สุด สามารถทำได้ทั้งหมดในลูปแบบง่าย ๆ พร้อมเงื่อนไขการสิ้นสุดแบบง่าย
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
ตอนนี้ลองแบบเดียวกันตามลำดับ โดยปกติคุณจะต้องมีตัวหารอื่นที่มีกำลังมากที่สุด 10 สำหรับหมายเลขที่ระบุ (ที่นี่ 100) คุณต้องพบสิ่งนี้ก่อน สิ่งอื่น ๆ อีกมากมายที่ต้องทำ
การแปลงสตริงเป็น int ทำได้ง่ายขึ้นใน พ.ศ. เมื่อมันเป็นการดำเนินการเขียนย้อนกลับ เขียนเก็บหลักสำคัญที่สุดล่าสุดดังนั้นควรอ่านก่อน
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');
}
ตอนนี้ทำเช่นเดียวกันในการสั่งซื้อ LE อีกครั้งคุณจะต้องมีปัจจัยเพิ่มเติมที่เริ่มต้นด้วย 1 และถูกคูณด้วย 10 สำหรับแต่ละหลัก
ดังนั้นฉันมักจะชอบที่จะใช้ BE สำหรับการจัดเก็บเพราะค่าถูกเขียนอย่างแน่นอนครั้งเดียว แต่อ่านอย่างน้อยหนึ่งครั้งและอาจจะหลายครั้ง สำหรับโครงสร้างที่เรียบง่ายกว่าฉันมักจะไปที่เส้นทางเพื่อแปลงเป็น LE แล้วกลับผลลัพธ์แม้ว่ามันจะเขียนค่าเป็นครั้งที่สองก็ตาม
อีกตัวอย่างสำหรับการจัดเก็บ BE จะเป็นการเข้ารหัสแบบ UTF-8 และอื่น ๆ อีกมากมาย