Rust, почему размер варианта перечисления (u32, u32) меньше, чем (u64)?

Изучал перечисления упаковки и при этом запускал следующую программу

enum SizeEnum {
    V1(u32, u32),
    // V2(u64),
    V3(u32, u32),
}

fn main() {
    println!("{:?}", std::mem::size_of::<SizeEnum>());
}

Ссылка на игровую площадку

На выходе получается 12 байт (96 бит). Я ожидал 16 байт (128 бит). Вот что происходит, когда я раскомментирую V2 вариант.

Вопросы:

  1. Так почему же вариант u32, u32 занимает меньше места, чем u64?
  2. И почему 12 байт (96 бит), а не что-то вроде 64+8 (72 бита)? Я предполагаю, что это что-то о дополнении, но был бы признателен за подробный ответ.

Мое предположение: 1) Поле u64 выравнивает его по 8 байтам. 2) Поля u32 выравнивают его по 4 байтам.

kotatsuyaki 12.12.2022 12:23
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
2
1
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Оба вопроса сводятся к выравниванию.

u32 должны быть выровнены по 4 байтам. u64 необходимо выровнять по 8 байтам.

Следовательно, для u32 есть 3 байта заполнения для дискриминанта (так что u32 находится в четвертом байте), а для u64 их семь.

Обратите внимание, что ничего из этого не указано. Это может варьироваться от архитектуры к архитектуре, от версии компилятора к версии компилятора или даже от запуска компилятора к запуску компилятора.

Sven Marnach 12.12.2022 14:14

Кроме того, дело не только в том, что u64 должен быть выровнен по 8, но и в том, что структуры выровнены в соответствии с их наибольшим элементом. Таким образом, с полем u64SizeEnum должно быть выровнено по 8 байтам, а его размер должен быть кратен его выравниванию. Имея только поля u32, его необходимо выровнять по 4, а его размер должен быть кратен 4.

Masklinn 12.12.2022 14:15

@SvenMarnach, как правило, выравнивание имеет тенденцию становиться более ограниченным в более странных архитектурах или в более конкретных целях (например, кадры стека, векторные буферы). Некоторые популярные архитектуры (в частности, x64, которым в современных процессорах все равно, пока вы не коснетесь SSE или не задействуете межъядерные коммуникации) хорошо справляются с невыровненными данными, но другим архитектурам это действительно не нравится.

Masklinn 12.12.2022 14:18

@Masklinn Я думаю, что это не столько об архитектуре, сколько об ABI. Как вы уже заметили, x86 в значительной степени не заботится о выравнивании, и оно все еще применяется компилятором. Другим примером является то, что 64-битные значения, как правило, по-прежнему выравниваются по 8 байтам в 32-битных архитектурах, даже несмотря на то, что они имеют только 32-битную шину данных, поэтому значения в любом случае должны извлекаться из двух половин. На практике выравнивание такое, как указано в этом ответе. Я хотел сказать, что как программист вы не должны полагаться на это.

Sven Marnach 12.12.2022 17:56

@SvenMarnach, это абсолютно архитектура, выравнивание вызывает проблемы для ISA (хотя они также могут вызывать проблемы для ABI). Согласно старой статье lwn на самом деле были ISA, которые не могли выполнять невыровненный доступ, но не сообщали вам об этом, вместо этого они просто возвращали другой, выровненный доступ к памяти. x86, как известно, снисходителен к невыровненному доступу, а SSE - нет и обычно будет SEGV (хотя, по-видимому, в AVX были некоторые послабления при загрузке и сохранении SSE).

Masklinn 12.12.2022 21:52

Конечно, это тоже архитектура, но если бы это была только архитектура, мы бы увидели гораздо более мягкие правила выравнивания.

Sven Marnach 12.12.2022 22:01

Другие вопросы по теме