C ++ Выравнивание памяти в пользовательском распределителе стека - PullRequest
3 голосов
/ 30 августа 2011

Обычно данные выровнены по мощности двух адресов в зависимости от их размера.

Как мне выровнять структуру или класс размером 20 байт или другой размер, не равный двум?

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

У меня есть класс Allocator, который использует malloc () для выделения большого количества данных.Затем я использую метод void * allocate (U32 size_of_object), чтобы вернуть указатель на то, где я могу хранить объекты, которые мне нужно хранить.Таким образом, все объекты хранятся в одной и той же области памяти, и мы надеемся, что они будут помещаться в кэш, уменьшая количество кешей.

Ответы [ 6 ]

3 голосов
/ 30 августа 2011

C ++ 11 имеет оператор alignof специально для этой цели.Не используйте какие-либо приемы, упомянутые в других публикациях, так как все они имеют крайние случаи или могут потерпеть неудачу для определенных оптимизаций компилятора.Оператор alignof реализован компилятором и знает точное используемое выравнивание.

См. Это описание нового оператора выравнивания в c ++ 11

3 голосов
/ 30 августа 2011

Хотя компилятор (или интерпретатор) обычно выделяет отдельные элементы данных на выровненных границах, структуры данных часто имеют элементы с различными требованиями к выравниванию. Для обеспечения правильного выравнивания транслятор обычно вставляет дополнительные безымянные элементы данных, чтобы каждый элемент был правильно выровнен. Кроме того, структура данных в целом может быть дополнена последним неназванным членом. Это позволяет правильно выровнять каждый член массива структур. http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86

Это говорит о том, что компилятор позаботится об этом за вас, 99,9% времени. Что касается того, как заставить объект выравниваться определенным образом, это зависит от компилятора и работает только при определенных обстоятельствах.

MSVC: http://msdn.microsoft.com/en-us/library/83ythb65.aspx

__declspec(align(20)) 
struct S{ int a, b, c, d; };
//must be less than or equal to 20 bytes

GCC: http://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/Type-Attributes.html

struct S{ int a, b, c, d; } 
__attribute__ ((aligned (20)));

Я не знаю кроссплатформенного способа (включая макросы!), Чтобы сделать это, но, возможно, где-то есть аккуратный макрос.

1 голос
/ 30 августа 2011

Поскольку вы добавили, что действительно хотите написать свой собственный распределитель, ответ прост: просто убедитесь, что ваш распределитель возвращает указатель, значение которого кратно запрошенному размеру. Размер самого объекта уже будет соответствующим образом скорректирован (через внутреннее заполнение), так что все объекты-члены сами выровнены должным образом, поэтому, если вы запрашиваете sizeof(T) байт, все, что нужно вашему распределителю, - это вернуть указатель, значение которого делится на sizeof(T).

Если ваш объект действительно имеет размер 20 (как сообщает sizeof), вам больше не о чем беспокоиться. (На 64-битной платформе объект, вероятно, будет дополнен до 24 байтов.)

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

1 голос
/ 30 августа 2011

Из-за того, как работают шины данных процессора, вам нужно избегать «неправильного выравнивания» доступа. Обычно вы можете прочитать 32-битное значение за один раз с адресов, кратных четырем; если вы попытаетесь прочитать его по адресу, который не является таким кратным, процессору, возможно, придется собрать его двумя или более частями. Так что, если вы действительно беспокоитесь о вещах на этом уровне детализации, вам нужно беспокоиться не столько о структуре в целом, сколько о ее частях. Вы обнаружите, что компиляторы часто дополняют структуры фиктивными байтами для обеспечения согласованного доступа, если только вы специально не принудите их не использовать прагму.

1 голос
/ 30 августа 2011

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

0 голосов
/ 30 августа 2011

Как выровнять структуру или класс размером 20 байт или другим размером не-степени-два?

Выравнивание зависит от процессора, поэтому ответа нетна этот вопрос, по крайней мере, без знания целевого процессора.

Вообще говоря, выравнивание - это не то, о чем вам нужно беспокоиться;у вашего компилятора будут реализованы правила для вас.Это происходит время от времени, как при написании распределителя.Классическое решение обсуждается в Языке программирования C (K & R): используйте наихудшее выравнивание.malloc делает это, хотя он формулируется как , «указатель, возвращаемый в случае успешного выделения, должен быть соответствующим образом выровнен, чтобы его можно было назначить указателю на любой тип объекта».

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

typedef Align long;
union header {
    // the inner struct has the important bookeeping info
    struct {
        unsigned size;
        header* next; 
    } s;
    // the align member only exists to make sure header_t's are always allocated
    // using the alignment of a long, which is probably the worst alignment
    // for the target architecture ("worst" == "strictest," something that meets
    // the worst alignment will also meet all better alignment requirements)
    Align align;
};

Память выделяетсясоздание массива (используя что-то вроде sbrk()) размером header s, достаточно большого для удовлетворения запроса, плюс один дополнительный элемент header, который фактически содержит бухгалтерскую информацию.Если массив называется arry, бухгалтерская информация находится на arry[0], в то время как возвращаемый указатель указывает на arry[1] (элемент next предназначен для обхода свободного списка).

Это работает, но может привести к потере впустую пространства («В JSM Sun HotSpot хранилище объектов выравнивается по ближайшей 64-битной границе»).Мне известен лучший подход , который пытается получить выравнивание, специфичное для типа, а не "выравнивание, которое будет работать для чего-либо".

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

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