Изучал перечисления упаковки и при этом запускал следующую программу
enum SizeEnum {
V1(u32, u32),
// V2(u64),
V3(u32, u32),
}
fn main() {
println!("{:?}", std::mem::size_of::<SizeEnum>());
}
На выходе получается 12 байт (96 бит). Я ожидал 16 байт (128 бит). Вот что происходит, когда я раскомментирую V2
вариант.
Вопросы:
u32, u32
занимает меньше места, чем u64
?Оба вопроса сводятся к выравниванию.
u32
должны быть выровнены по 4 байтам. u64
необходимо выровнять по 8 байтам.
Следовательно, для u32
есть 3 байта заполнения для дискриминанта (так что u32
находится в четвертом байте), а для u64
их семь.
Обратите внимание, что ничего из этого не указано. Это может варьироваться от архитектуры к архитектуре, от версии компилятора к версии компилятора или даже от запуска компилятора к запуску компилятора.
Кроме того, дело не только в том, что u64 должен быть выровнен по 8, но и в том, что структуры выровнены в соответствии с их наибольшим элементом. Таким образом, с полем u64
SizeEnum
должно быть выровнено по 8 байтам, а его размер должен быть кратен его выравниванию. Имея только поля u32, его необходимо выровнять по 4, а его размер должен быть кратен 4.
@SvenMarnach, как правило, выравнивание имеет тенденцию становиться более ограниченным в более странных архитектурах или в более конкретных целях (например, кадры стека, векторные буферы). Некоторые популярные архитектуры (в частности, x64, которым в современных процессорах все равно, пока вы не коснетесь SSE или не задействуете межъядерные коммуникации) хорошо справляются с невыровненными данными, но другим архитектурам это действительно не нравится.
@Masklinn Я думаю, что это не столько об архитектуре, сколько об ABI. Как вы уже заметили, x86 в значительной степени не заботится о выравнивании, и оно все еще применяется компилятором. Другим примером является то, что 64-битные значения, как правило, по-прежнему выравниваются по 8 байтам в 32-битных архитектурах, даже несмотря на то, что они имеют только 32-битную шину данных, поэтому значения в любом случае должны извлекаться из двух половин. На практике выравнивание такое, как указано в этом ответе. Я хотел сказать, что как программист вы не должны полагаться на это.
@SvenMarnach, это абсолютно архитектура, выравнивание вызывает проблемы для ISA (хотя они также могут вызывать проблемы для ABI). Согласно старой статье lwn на самом деле были ISA, которые не могли выполнять невыровненный доступ, но не сообщали вам об этом, вместо этого они просто возвращали другой, выровненный доступ к памяти. x86, как известно, снисходителен к невыровненному доступу, а SSE - нет и обычно будет SEGV (хотя, по-видимому, в AVX были некоторые послабления при загрузке и сохранении SSE).
Конечно, это тоже архитектура, но если бы это была только архитектура, мы бы увидели гораздо более мягкие правила выравнивания.
Мое предположение: 1) Поле
u64
выравнивает его по 8 байтам. 2) Поляu32
выравнивают его по 4 байтам.