Почему компиляторы накладывают структуры на C / C ++? - PullRequest
1 голос
/ 07 августа 2020

Я узнал о заполнении структуры и прочитал, что причина заполнения структуры заключается в том, что если члены структуры не выровнены, процессор не сможет читать / записывать их только за один цикл. В общем случае расположение типа данных, состоящего из N байтов, должно быть по адресу, кратному N.

Предположим, что эта структура, например:

struct X
{
    char c;
    // 3 bytes padding here so that i is aligned.
    int i;
};

Здесь размер этой структуры должен быть 8 байтов, c выровнен по умолчанию, потому что он занимает только 1 байт, а i - нет. Для i нам нужно добавить 3 байта заполнения перед ним, чтобы он «выровнялся» и мог быть доступен только за один цикл. Скажите, если что-то не хватает.

1 - Как работает выравнивание? По чему выравниваются члены?

2 - Что лучше для ЦП получить доступ к типу данных N байтов, расположенному по адресу, кратному N? Почему, например, в структуре выше, если i находится по адресу XXX3 (оканчивается на 3, другими словами, не кратно 4), почему бы не прочитать слово, начинающееся с адреса XXX3? Почему оно должно быть кратным 4? Большинство процессоров обращаются к адресам, кратным размеру слова? Я считаю, что процессоры могут читать слово из памяти, начиная с любого байта. Я ошибаюсь?

3 - Почему компилятор не переупорядочивает элементы так, чтобы они занимали как можно больше места? Имеет ли значение заказ? Я не уверен, использует ли кто-нибудь фактические номера смещения для доступа к членам. Это означает, что при наличии структуры X x обычно доступ к членам осуществляется следующим образом: x.i не *(&x + 4). В последнем случае порядок действительно имеет значение, но в первом случае (который, я думаю, все используют) порядок не имеет значения. Я должен отметить, что в этом примере это не имеет значения, поскольку если i также предшествует c, в конце будет 3 байта заполнения. Я обычно спрашиваю, почему?

4 - Я читал, что это больше не важно и что процессоры теперь обычно могут обращаться к невыровненным членам, занимая то же время, что и выровненные. Это правда? Если да, то почему?

наконец, если есть хорошее место, чтобы узнать больше, я был бы благодарен.

Ответы [ 3 ]

4 голосов
/ 07 августа 2020

C и C ++ не являются совместимыми тегами. Выберите один.

Процессору требуется меньше logi c для доступа к естественно выровненному объекту, чем к невыровненному объекту.

Это может показаться ответом 1970-х годов, но немного перемотать вперед представьте себе загрузку 4-байтового количества с адреса 0x1ffffff.

Что именно делает ЦП? Спросите у системы памяти байт по адресу 0x1ffffff, затем длинный по адресу 0x2000000, затем сдвиньте и замаскируйте их в регистр?

Звучит неплохо, пока вы не поймете, что для этого потребовались две отдельные транзакции памяти. выполнить это. Это плохо. Другой ЦП мог бы частично переписать это в промежуточной операции, поэтому наша нагрузка недействительна.

Расширение протокола блокировки шины для обработки нескольких транзакций, вероятно, не принесет результата: потребовалось много работы, чтобы заставить протоколы шины работать как есть.

На практике современные системы использовали доступ с выравниванием по кешу, поэтому при условии, что ваш невыровненный доступ находится в строке кеша, это, вероятно, нормально, но если это не так, вы находятся во власти неуказанных контроллеров шины и т. д. c ...

3 голосов
/ 07 августа 2020

1 Как работает выравнивание?

Память для объектов выделяется в тех местах памяти, где выполняется требование выравнивания типа. То есть: для требования выравнивания N адрес ячейки памяти будет делиться на N.

1 По чему выравниваются элементы?

Объекты выровнены с любым выравниванием типа этого объекта в целевой системе. Это то же самое для всех объектов, включая объекты-члены.

2 Обращается ли большинство ЦП к адресам, кратным размеру слова?

Некоторые Процессоры действительно имеют доступ только к выровненным адресам.

2 Я считаю, что процессоры могут читать слово из памяти, начиная с любого байта. Я не прав?

В случае с каким-то процессором вы не ошибаетесь. Было бы неправильно полагать, что это применимо ко всем ЦП.

2 - Что лучше для ЦП получить доступ к типу данных N байтов, расположенному по адресу, кратному N?

На таком CPU, как упомянуто выше, адрес чтения, не кратный N (т.е. выровненный), приведет к сбою сегментации. Ошибка сегментации приведет к завершению процесса. Лучше, чтобы процесс не завершался до тех пор, пока он не завершит все, что должен был сделать.

На некоторых других процессорах доступ к памяти с выровненного адреса может быть быстрее. Быстрее - лучше.

Вероятно, на всех процессорах доступ к смещенной памяти не будет операцией atomi c. Лучше это или нет, зависит от того, что вы делаете.

3 - Почему компилятор не переупорядочивает элементы так, чтобы они занимали как можно больше места? Я не уверен, использует ли кто-нибудь фактические номера смещения для доступа к элементам.

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

Однако гарантии для программистов не обязательно являются единственной проблемой с произвольным порядком членов. Другой аспект - совместимость библиотек с отдельными компиляторами. Все задействованные компиляторы должны согласовать порядок членов. Указанный порядок - это порядок объявления.

4 - Я читал, что это больше не важно и что теперь процессоры обычно могут получать доступ к невыровненным элементам, занимая то же время, что и выровненные. Это правда? Если да, то почему?

Это чрезмерно обобщенное утверждение. Это может быть верно для некоторых процессоров в некоторых случаях использования. Я рекомендую не считать такое общее утверждение универсальной истиной.

Если бы мы предположили, что это верно для определенного c ЦП, причиной этого могло бы быть то, что такой новый ЦП может получить доступ к невыровненным память, в то время как более ранний процессор не мог (например, старый ARMv4).

В другом случае, когда более ранний процессор мог, возможно, читать и писать без выравнивания, но такие операции могли быть медленнее. Если бы на новом ЦП операции имели эквивалентную скорость, то выравнивание могло бы стать неважным.

Старые ЦП все еще используются и не исчезли.

3 голосов
/ 07 августа 2020
  1. Они выравниваются как минимум по _Alignof(type). В принципе, реализация может быть выровнена дальше, но это, как правило, нежелательно, и никакая основная реализация не делает этого.

  2. Как отмечено в комментарии Элджая (выделено мной):

    Выравнивание - это ограничение архитектуры платформы . Несогласованный доступ к данным может быть дорогостоящим (до x16 дороже по производительности), чем согласованный доступ на некоторых архитектурах, и может помешать atomi c чтение / запись (актуально только для многопоточных приложений), или быть полностью неподдерживаемым (вызывая ошибку процесса). Другие архитектуры могут справиться с ними без проблем, а другие архитектуры могут справиться с ними, но со снижением производительности (поэтому компилятор ошибается в отношении производительности).

    Стандарт языка написан для поддержки такой платформы ограничения.

  3. Это не разрешено, по крайней мере, если адрес структуры взят таким образом, чтобы сделать представление видимым для приложения. Спецификация языка требует, чтобы члены были в порядке. Это 6.7.2.1 Спецификаторы структуры и объединения, ¶15 :

    Внутри объекта структуры не-битовые поля и единицы, в которых находятся битовые поля, имеют адреса, которые увеличиваются в порядке их объявления. Указатель на объект структуры, преобразованный соответствующим образом, указывает на его начальный член (или, если этот член является битовым полем, то на модуль, в котором он находится), и наоборот. Внутри объекта структуры может быть безымянный отступ, но не в его начале.

  4. Нет, это неправда. Высокопроизводительные процессоры обычно исправляют несогласованный доступ прозрачно, чтобы разрешить определенные типы неаккуратного кода, а также операции, которые обязательно смещены (например, memcpy или memmove через буферы с различным выравниванием), но это не меняет того факта, что эти операции имеют тенденцию быть более дорогими и недоступны для некоторых вещей, таких как операции atomi c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...