เมื่อการเขียนโปรแกรมใน CI พบว่ามันมีค่ามากที่จะแพ็ค structs โดยใช้ GCCs __attribute__((__packed__))
[... ]
เมื่อคุณพูดถึง__attribute__((__packed__))
ฉันถือว่าคุณมีความตั้งใจที่จะกำจัดช่องว่างภายในstruct
(ทำให้สมาชิกแต่ละคนมีการจัดตำแหน่งแบบ 1 ไบต์)
ไม่มีมาตรฐานสำหรับโครงสร้างการจัดวางที่ทำงานได้ในคอมไพเลอร์ C ทั้งหมดหรือไม่?
... และคำตอบคือ "ไม่" การแพ็ดดิ้งและการจัดเรียงข้อมูลสัมพันธ์กับ struct (และอาร์เรย์ที่ต่อเนื่องกันของ structs ในสแต็กหรือฮีป) มีอยู่ด้วยเหตุผลสำคัญ ในหลาย ๆ เครื่องการเข้าถึงหน่วยความจำที่ไม่ได้จัดแนวอาจทำให้ประสิทธิภาพลดลงอย่างมีนัยสำคัญ (แม้ว่าจะมีฮาร์ดแวร์ใหม่น้อยลง) ในบางกรณีที่เกิดขึ้นได้ยากการเข้าถึงหน่วยความจำที่ไม่ตรงแนวทำให้เกิดข้อผิดพลาดของบัสที่ไม่สามารถกู้คืนได้ (อาจทำให้ระบบปฏิบัติการทั้งหมดล้มเหลว)
เนื่องจากมาตรฐาน C มุ่งเน้นไปที่การพกพาจึงมีความรู้สึกเพียงเล็กน้อยที่จะมีวิธีมาตรฐานในการกำจัดการขยายทั้งหมดในโครงสร้างและเพียงแค่อนุญาตให้มีการวางแนวโดยพลการเนื่องจากมีความเสี่ยงที่จะทำให้รหัส C ไม่ใช่แบบพกพา
วิธีที่ปลอดภัยที่สุดและแบบพกพามากที่สุดเพื่อให้ข้อมูลดังกล่าวส่งออกไปยังแหล่งข้อมูลภายนอกในทางที่จะช่วยลดช่องว่างทั้งหมดคือการเป็นอันดับจาก / structs
ไปยังลำธารไบต์แทนเพียงแค่พยายามที่จะส่งผ่านเนื้อหาหน่วยความจำดิบของคุณ สิ่งนี้ยังป้องกันโปรแกรมของคุณจากการถูกลงโทษด้านประสิทธิภาพนอกบริบทการทำให้เป็นอนุกรมและจะช่วยให้คุณเพิ่มเขตข้อมูลใหม่ได้อย่างอิสระstruct
โดยไม่ต้องละทิ้งและรวมซอฟต์แวร์ทั้งหมด นอกจากนี้ยังจะช่วยให้คุณมีพื้นที่ในการจัดการกับสิ่งต่าง ๆ และสิ่งเหล่านั้นถ้าเป็นเช่นนั้น
มีวิธีหนึ่งในการกำจัดการขยายทั้งหมดโดยไม่เข้าถึงสำหรับคำสั่งเฉพาะของคอมไพเลอร์แม้ว่าจะใช้ได้เฉพาะในกรณีที่ลำดับสัมพัทธ์ระหว่างฟิลด์ไม่สำคัญ รับบางสิ่งเช่นนี้
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... เราต้องการช่องว่างสำหรับการเข้าถึงหน่วยความจำที่สัมพันธ์กันกับที่อยู่ของโครงสร้างที่มีฟิลด์เหล่านี้เช่น:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... ซึ่ง.
หมายถึงช่องว่างภายใน ทุกคนx
จะต้องสอดคล้องกับขอบเขต 8 ไบต์สำหรับประสิทธิภาพ (และบางครั้งพฤติกรรมที่ถูกต้อง)
คุณสามารถกำจัดช่องว่างภายในแบบพกพาโดยใช้การแสดง SOA (โครงสร้างของอาร์เรย์) เช่นนี้ (สมมติว่าเราต้องการFoo
อินสแตนซ์8 ตัว):
struct Foos
{
double x[8];
char y[8];
};
เราทำลายโครงสร้างอย่างมีประสิทธิภาพ ในกรณีนี้การแสดงหน่วยความจำจะเป็นดังนี้:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... และนี่:
01234567
yyyyyyyy
... ไม่มีช่องว่างภายในและไม่เกี่ยวข้องกับการเข้าถึงหน่วยความจำที่ไม่ตรงแนวเนื่องจากเราไม่ได้เข้าถึงเขตข้อมูลเหล่านี้เป็นออฟเซ็ตของโครงสร้างที่อยู่ แต่แทนที่จะเป็นออฟเซ็ตฐานที่อยู่สำหรับอาร์เรย์ที่มีประสิทธิภาพ
นอกจากนี้ยังนำโบนัสของการเข้าถึงลำดับที่รวดเร็วขึ้นซึ่งเป็นผลมาจากข้อมูลที่น้อยลงในการใช้งาน (ไม่มีช่องว่างที่ไม่เกี่ยวข้องในการผสมเพื่อชะลออัตราการใช้ข้อมูลที่เกี่ยวข้องของเครื่อง) และยังเป็นไปได้ที่คอมไพเลอร์ .
ข้อเสียคือมันเป็นรหัส PITA นอกจากนี้ยังอาจมีประสิทธิภาพน้อยลงสำหรับการเข้าถึงแบบสุ่มด้วยก้าวที่ใหญ่กว่าในระหว่างเขตข้อมูลซึ่งบ่อยครั้งที่ตัวแทน AoS หรือ AoSoA จะทำได้ดีกว่า แต่นั่นเป็นวิธีมาตรฐานวิธีหนึ่งในการขจัดช่องว่างภายในและจัดเก็บสิ่งต่าง ๆ ให้แน่นที่สุดเท่าที่จะทำได้โดยไม่ต้องขันด้วยการจัดตำแหน่งของทุกสิ่ง