Я написала CMyObject
Класс следующим образом:
class CMyObject
{
public:
CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};
public:
ULONGLONG m_uField1;
UINT m_uField2;
BOOL m_bField3;
int m_iField4;
BOOL m_bField5;
}
Чтобы уменьшить размер CMyObject
Я изменил это на:
class CMyObject
{
public:
CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};
public:
ULONGLONG m_uField1;
UINT m_uField2;
short m_sField4; // Change from int to short, since short is enough for the data range
unsigned m_bField3: 1; // Change field 3 and 5 from BOOL to bit field to save spaces
unsigned m_bField5: 1;
}
Тем не менее sizeof(CMyObject)
до сих пор не изменилось, почему?
Могу ли я использовать pargma pack(1)
в class
упаковать все переменные-члены, например так:
pargma pack(1)
class CMyObject
{
public:
CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};
public:
ULONGLONG m_uField1;
UINT m_uField2;
short m_sField4; // Change from int to short, since short is enough for the data range
unsigned m_bField3: 1; // Change field 3 and 5 from BOOL to bit field to save spaces
unsigned m_bField5: 1;
}
pargma pack(0)
Из-за вашего ULONGLONG
первый член, ваша структура будет иметь 8-байтовое (64-битное) выравнивание. Предполагая 32-битные числа, ваша первая версия использует 18 байтов, что займет 24 байта для хранения. (Большие и маленькие элементы перемежаются, что усугубляет ситуацию, но, по моим подсчетам, это не меняет ответа.) Ваша вторая версия также занимает 18 байт:
m_uField1
m_uField2
m_sField4
unsigned
битовые поля (которые будут иметь 4-байтовое выравнивание, поэтому также добавят 2 байта после заполнения m_sField4
)Если вы переключитесь на short unsigned m_bField3:1
а также short unsigned m_bField4:1
Я думаю, что есть большая вероятность, что ваша структура станет меньше и поместится всего в 16 байтов.
Использование #pragma pack
Я не могу комментировать специфику, но возможно, что это может уменьшить размер вашей структуры. Я предполагаю, что это может и не произойти, так как обычно лучше компенсировать неоптимальное упорядочение элементов с выравниванием, и, по моим подсчетам, ваши переменные слишком велики. (Однако, снятие требования выравнивания на ULONGLONG
может уменьшить размер вашей структуры в любой версии, и #pragma pack
может сделать это.)
Как упоминает @slater, чтобы уменьшить размер и отступы в вашей структуре, вы должны объявлять переменные-члены одинакового размера рядом друг с другом. Это очень хорошее правило — объявлять переменные в порядке уменьшения размера, что, как правило, сводит к минимуму заполнение и использование совпадающих требований выравнивания.
Тем не менее, размер структуры не всегда самая важная проблема. Члены инициализируются в порядке объявления в конструкторе, и для некоторых классов это имеет значение, поэтому вы должны принять это во внимание. Кроме того, если ваша структура охватывает несколько строк кэша и будет использоваться одновременно несколькими потоками, в идеале следует поместить переменные, которые используются вместе, рядом и в одной строке кэша, и переменные, которые не используются вместе, в отдельные строки кэша, чтобы уменьшить / исключить ложное. обмен.
Что касается вашего первого вопроса «Тем не менее, sizeof(CMyObject)
до сих пор не изменилось, почему?
Ваш BOOL
s не определены непрерывно, поэтому они дополняются компилятором для выравнивания памяти.
В большинстве 32-битных систем эта структура использует 16 байтов:
struct {
char b1; // 1 byte for char, 3 for padding
int i1; // 4 bytes
char b2; // 1 byte for char, 3 for padding
int i2; // 4 bytes
}
Эта структура использует 12 байтов:
struct {
char b1; // 1 byte
char b2; // 1 byte, 2 bytes for padding
int i1; // 4 bytes
int i2; // 4 bytes
}
Выравнивание и упаковка зависят от реализации, но обычно вы можете запросить меньшее выравнивание и лучшую упаковку, используя меньшие типы. Это также относится и к указанию меньших типов в объявлениях битовых полей, поскольку многие компиляторы интерпретируют тип битового поля как запрос на единицу выделения этого размера конкретно и требование выравнивания этого типа конкретно.
В вашем случае одна очевидная ошибка заключается в использовании unsigned
для битовых полей. использование unsigned char
для битовых полей, и он должен упаковать намного лучше
class CMyObject
{
public:
CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};
public:
ULONGLONG m_uField1;
UINT m_uField2;
short m_sField4;
unsigned char m_bField3: 1;
unsigned char m_bField5: 1;
};
Это не обязательно сделает его таким компактным, как #pragma pack(1)
может сделать это, но это будет гораздо ближе к нему.