[英]Why does this piece of code written using uint8_t run faster than analogous code written with uint32_t or uint64_t on a 64bit machine?
[英]Why does an uint64_t needs more memory than 2 uint32_t's when used in a class? And how to prevent this?
我以下面的代碼為例。
#include <iostream>
struct class1
{
uint8_t a;
uint8_t b;
uint16_t c;
uint32_t d;
uint32_t e;
uint32_t f;
uint32_t g;
};
struct class2
{
uint8_t a;
uint8_t b;
uint16_t c;
uint32_t d;
uint32_t e;
uint64_t f;
};
int main(){
std::cout << sizeof(class1) << std::endl;
std::cout << sizeof(class2) << std::endl;
std::cout << sizeof(uint64_t) << std::endl;
std::cout << sizeof(uint32_t) << std::endl;
}
印刷
20
24
8
4
所以很容易看出一個 uint64_t 和兩個 uint32_t 一樣大,為什么類 2 會有 4 個額外的字節,如果它們是相同的,除了用兩個 uint32_t 替換一個 uint64_t 之外。
正如有人指出的那樣,這是由於padding 。
為了防止這種情況,您可以使用
#pragma pack(1)
class ... {
};
#pragma pack(pop)
它告訴您的編譯器不要對齊 8 個字節,而是對齊 1 個字節。 pop 命令將其關閉(這非常重要,因為如果您在標題中執行此操作並且有人包含您的標題,則可能會發生非常奇怪的錯誤)
對齊規則(在 x86 和 x86_64 上)通常是對齊變量的 size 。
換句話說,32 位變量在 4 個字節上對齊,64 位變量在 8 個字節上對齊,等等。
f
的偏移量為 12,因此在uint32_t f
情況下不需要填充,但是當f
是uint64_t
,添加 4 個字節的填充以使f
對齊 8 個字節。
因此,最好將數據成員從大到小排序。 那么就不需要填充或包裝(除非可能在結構的末尾)。
為什么在類中使用時 uint64_t 比 2 個 uint32_t 需要更多內存?
原因是由於對齊要求而填充。
在大多數 64 位架構上,uint8_t 的對齊要求為 1,uint16_t 的對齊要求為 2,uint32_t 的對齊要求為 4,uint64_t 的對齊要求為 8。編譯器必須確保結構中的所有成員都正確對齊並且結構的大小是其整體對齊要求的倍數。 此外,編譯器不允許重新排序成員。
所以你的結構最終布局如下
struct class1
{
uint8_t a; //offset 0
uint8_t b; //offset 1
uint16_t c; //offset 2
uint32_t d; //offset 4
uint32_t e; //offset 8
uint32_t f; //offset 12
uint32_t g; //offset 16
}; //overall alignment requirement 4, overall size 20.
struct class2
{
uint8_t a; //offset 0
uint8_t b; //offset 1
uint16_t c; //offset 2
uint32_t d; //offset 4
uint32_t e; //offset 8
// 4 bytes of padding because f has an alignment requirement of 8
uint64_t f; //offset 16
}; //overall alignment requirement 8, overall size 24
以及如何防止這種情況?
不幸的是,沒有好的通用解決方案。
有時可以通過重新排序字段來減少填充量,但這對您的情況沒有幫助。 它只是在結構中移動填充。 具有需要 8 字節對齊的字段的結構的大小始終是 8 的倍數。 因此,無論您重新排列字段多少,您的結構的大小始終至少為 24。
您可以使用特定於編譯器的功能,例如#pragma pack
或__attribute((packed))
來強制編譯器比正常對齊要求所允許的更緊密地打包結構。 然而,除了限制可移植性之外,這在獲取成員地址或綁定對成員的引用時會產生問題。 結果指針或引用可能不滿足對齊要求,因此使用起來可能不安全。
不同的編譯器處理這個問題的方式各不相同。 來自一些在 Godbolt 上玩耍的人。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.