Ответ 1
Короткий ответ заключается в том, что базовые объекты в процессорах имеют размеры, которые являются малыми степенями двух (например, 1, 2, 4, 8 и 16 байт), а память организована в группы, размер которых малой мощности двух ( например, 8 байтов), поэтому структуры должны быть выровнены, чтобы хорошо работать с этими размерами.
Долгий ответ заключается в том, что причины этого основаны на физике и элементарной математике. Компьютеры, естественно, работают с битами со значениями 0 и 1. Это связано с тем, что легко создавать физические вещи, которые переключаются между двумя значениями: высокое напряжение и низкое напряжение, наличие заряда или отсутствие заряда и т.д. Отличие между тремя значениями сложнее, потому что вы должны быть более чувствительными к переходам между значениями. Таким образом, поскольку компьютерные технологии развивались на протяжении десятилетий, мы использовали биты (двоичные цифры) вместо альтернатив, таких как цифровые цифры.
Чтобы сделать большие числа, мы объединяем несколько бит. Таким образом, два бита могут, объединены, иметь четыре значения. Три бита могут иметь восемь значений и т.д. В старых компьютерах иногда бит составлял шесть или десять групп одновременно. Тем не менее, восемь стали обычными, и в настоящее время они являются стандартными. Использование восьми бит для байта не имеет такой сильной физической причины, как некоторые другие группы, которые я описываю, но это путь мира.
Еще одна особенность компьютеров - память. После того, как у нас есть эти байты, мы хотим сохранить их много в устройстве, которое легко доступно для процессора, поэтому мы можем быстро получить много байтов в процессор и из него. Когда у нас много байтов, нам нужно, чтобы процессор сообщал памяти, какие байты процессор хочет читать или писать. Таким образом, процессору нужен способ обращения к байтам.
Процессор использует биты для значений, поэтому он будет использовать биты для значений адреса. Таким образом, память будет построена так, чтобы принимать биты, чтобы указать, какие байты будут поставляться процессору при чтении процессора или какие байты хранить при записи процессора. Что делает устройство памяти с этими битами? Легко использовать один бит для управления одним коммутатором путей в память. Память будет сделана из множества мелких частей, в которых хранятся байты.
Рассмотрим вещь в устройстве памяти, которая может хранить байт, и рассмотрите две из этих вещей рядом друг с другом, скажем A и B. Мы можем использовать переключатель, чтобы выбрать, хотим ли мы, чтобы байт был активным, или B байт. Теперь рассмотрим четыре из этих вещей: A, B, C и D. Мы можем использовать один переключатель, чтобы выбрать, использовать ли группу A-B или использовать группу C-D. Затем другой переключатель выбирает A или B (если используется группа A-B) или C или D (если используется группа C-D).
Этот процесс продолжается: каждый бит в адресе памяти выбирает группу используемых единиц хранения. 1 бит выбирает между двумя запоминающими устройствами, 2 выбирает между 4, 3 выбирает между 8, 4 выбирает между 16 и так далее. 8 бит выбирают между 256 единицами хранения, 24 бита выбирают между 16 777 216 единицами хранения и 32 битами выбирают между 4 294 967 296 единицами хранения.
Есть еще одно осложнение. Перемещение отдельных байтов между процессором и памятью происходит медленно. Вместо этого современные компьютеры организуют память на более крупные части, например восемь байтов. Вы можете перемещать только восемь байтов за раз между памятью и процессором. Когда процессор запрашивает, что память снабжает некоторыми данными, процессор посылает только старшие биты адреса - младшие три бита выбирают отдельные байты в течение восьми байтов и не отправляются в память.
Это быстрее, потому что процессор получает восемь байтов за время, которое в противном случае потребовалось бы для того, чтобы память делала все свое переключение на поставку одного байта, и это дешевле, потому что вам не нужно огромное количество дополнительных переключателей, которые потребуются для выделения отдельных байтов в памяти.
Однако теперь это означает, что у процессора нет возможности получить отдельный байт из памяти. Когда вы выполняете инструкцию, которая обращается к отдельному байту, процессор должен считывать восемь байтов из памяти, а затем перемещать эти байты внутри процессора, чтобы получить один байт, который вы хотите. Аналогично, чтобы получить два или четыре байта, процессор считывает восемь байтов и извлекает только нужные вам байты.
Чтобы упростить этот процесс, разработчики процессоров указывают, что данные должны быть выровнены определенным образом. Как правило, они требуют, чтобы двухбайтовые данные (например, 16-разрядные целые числа) были выровнены по краям двух байтов, четырехбайтовые данные (например, 32-битные целые числа и 32-разрядные значения с плавающей запятой), которые были выровнены по кратным четырем байтов и восьмибайтовых данных, которые должны быть выровнены по кратным восьми байтам.
Требуемое выравнивание имеет два эффекта. Во-первых, поскольку четырехбайтовые данные могут начинаться только в двух местах в восьмибайтовом фрагменте, считываемом из памяти (в начале или в середине), разработчикам процессоров нужно только вставлять провода для извлечения четырех байтов из двух мест. Им не нужно добавлять все лишние проводы, чтобы извлечь четыре байта из любого из восьми отдельных байтов, которые могли бы быть начальными, если бы было разрешено какое-либо выравнивание. (Некоторые процессоры полностью запретят загрузку не выровненных данных, а некоторые процессоры позволят использовать его, но используют медленные методы для извлечения, которые используют меньшее количество проводов, но используют итеративный алгоритм для переноса данных по нескольким циклам процессора, поэтому невысокие нагрузки медленны.)
Второй эффект заключается в том, что, поскольку четырехбайтовые данные могут начинаться только в двух местах в восьмибайтовом куске, он также заканчивается внутри этого фрагмента. Подумайте, что произойдет, если вы попытаетесь загрузить четыре байта данных, которые начались в шестом байте восьмибайтового фрагмента. Первые два байта находятся в куске, но следующие два байта находятся в следующем фрагменте в памяти. Процессору пришлось бы считывать два куска из памяти, брать с каждого из них разные байты и объединять эти байты. Это намного медленнее, чем просто чтение одного фрагмента.
Таким образом, память организована силами двух, потому что это естественный результат бит, а процессоры требуют выравнивания, потому что это делает доступ к памяти более эффективным. Выравнивание, естественно, равно двум, и поэтому размеры вашей структуры лучше работают, когда они кратно мощностям двух, которые используются для выравнивания.