Старые K&R (2-е изд.) И другие тексты на языке C, которые я читал, обсуждают реализацию распределителя динамической памяти в стиле malloc() и free(), обычно также мимоходом упоминают кое-что об ограничениях на выравнивание типов данных. Очевидно, что определенные аппаратные архитектуры компьютеров (ЦП, регистры и доступ к памяти) ограничивают способы хранения и адресации определенных типов значений. Например, может быть требование, чтобы 4-байтовое (long) целое число сохранялось, начиная с адресов, кратных четырем.
Какие ограничения, если таковые имеются, накладывают основные платформы (Intel и AMD, SPARC, Alpha) на распределение памяти и доступ к памяти, или я могу спокойно игнорировать выравнивание выделения памяти по определенным границам адресов?





Выравнивание по-прежнему очень важно сегодня. Некоторые процессоры (на ум приходит семейство 68k) выдают исключение, если вы пытаетесь получить доступ к значению слова на нечетной границе. Сегодня большинство процессоров запускают два цикла памяти для выборки невыровненного слова, но это определенно будет медленнее, чем выровненная выборка. Некоторые другие процессоры даже не генерируют исключение, а извлекают значение неверный из памяти!
Если не по какой-либо другой причине, кроме производительности, разумно попытаться следовать настройкам выравнивания вашего процессора. Обычно ваш компилятор позаботится обо всех деталях, но если вы делаете что-либо, когда вы сами выкладываете структуру памяти, то об этом стоит подумать.
Вам все равно нужно знать о проблемах выравнивания при компоновке класса или структуры на C (++). В этих случаях компилятор сделает для вас правильные вещи, но общий размер структуры / класса может быть более расточительным, чем необходимо.
Например:
struct
{
char A;
int B;
char C;
int D;
};
Имел бы размер 4 * 4 = 16 байт (предположим, что Windows на x86), тогда как
struct
{
char A;
char C;
int B;
int D;
};
Имел бы размер 4 * 3 = 12 байт.
Это связано с тем, что компилятор обеспечивает выравнивание по 4 байта для целых чисел, но только 1 байт для символов.
Как правило, объединяйте переменные-члены одного размера (типа) вместе, чтобы минимизировать бесполезное пространство.
Как упоминал Грег, это все еще важно сегодня (возможно, в некотором смысле даже в большей степени), и компиляторы обычно заботятся о выравнивании в зависимости от цели архитектуры. В управляемых средах JIT-компилятор может оптимизировать выравнивание на основе архитектуры времени выполнения.
Вы можете увидеть директивы pragma (в C / C++), которые изменяют выравнивание. Это следует использовать только тогда, когда требуется очень конкретное выравнивание.
// For example, this changes the pack to 2 byte alignment.
#pragma pack(2)
Sparc, MIPS, Alpha и большинство других «классических RISC-архитектур» даже сегодня допускают только согласованный доступ к памяти. Невыровненный доступ вызовет исключение, но некоторые операционные системы будут обрабатывать исключение путем копирования с желаемого адреса в программное обеспечение с использованием меньших загрузок и хранилищ. Код приложения не узнает о проблеме, за исключением того, что производительность будет очень низкой.
MIPS имеет специальные инструкции (lwl и lwr), которые можно использовать для доступа к 32-битным величинам с невыровненных адресов. Когда компилятор может сказать, что адрес, скорее всего, не выровнен, он будет использовать эту последовательность из двух инструкций вместо обычной инструкции lw.
x86 может обрабатывать невыровненные обращения к памяти аппаратно без исключения, но по-прежнему наблюдается снижение производительности до 3 раз по сравнению с выровненными доступами.
Ульрих Дреппер написал исчерпывающий документ по этой и другим темам, связанным с памятью, Что каждый программист должен знать о памяти. Это очень длинная рецензия, но полная жевательных качеств.
Обратите внимание, что даже на IA-32 и AMD64 некоторые инструкции / встроенные функции SSE требуют согласованных данных. Эти инструкции вызовут исключение, если данные не выровнены, поэтому, по крайней мере, вам не придется отлаживать ошибки «неправильных данных». Есть и эквивалентные невыровненные инструкции, но, как говорит Дентон, они медленнее.
Если вы используете VC++, то помимо директив #pragma pack у вас также есть директивы __declspec (align) для точного выравнивания. В документации VC++ также упоминается функция __aligned_malloc для конкретных требований к выравниванию.
Как правило, если вы не перемещаете данные между компиляторами / языками или не используете инструкции SSE, вы, вероятно, можете игнорировать проблемы с выравниванием.