Что следует учитывать при выравнивании при проектировании пула памяти? - PullRequest
7 голосов
/ 20 августа 2010

Я работаю над пулом памяти для небольшого игрового движка.

Основное использование будет в качестве отдельного хранилища; пул содержит объект определенного типа и размера. В настоящее время пулы могут использоваться для хранения чего угодно, но распределение будет осуществляться в блоках определенного размера. Большая часть памяти будет выделена сразу, но при необходимости можно включить «разрастание», чтобы помочь в настройке (почти фиксированный размер).

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

Я разрешаю пользователю (мне) указать желаемый размер блоков, который в отдельном случае хранения будет размером объектов, которые я собираюсь в нем хранить.

Текущий подход состоит в том, чтобы выделить кусок памяти blocks * (desired_size + header_size) большой и поместить в него объекты с заголовком для каждого блока; объекты, очевидно, будут расположены непосредственно за этим заголовком.

Что мне нужно учитывать в отношении выравнивания памяти в моем сценарии?

Ответ, который я до сих пор придумал, заключается в том, что, пока desired_size представляет n -байтно выровненные данные; заголовок правильно выровнен и упакован компилятором, а также фактическими данными, все, что хранится в блоке, будет выровнено n .

n - это любая граница, требуемая платформой. На данный момент я нацеливаюсь на x86, но я не люблю делать предположения о платформе в моем коде.

Некоторые из ресурсов, которые я использовал:

Редактировать

Загружен небольшой пример кода, который может пригодиться кому-нибудь так же смущенному, как и мне, в будущем здесь .

Ответы [ 5 ]

7 голосов
/ 20 августа 2010

Распределения с malloc гарантированно выровнены для любого типа, предоставленного компилятором и, следовательно, для любого объекта [*].

Опасность заключается в том, что ваш заголовок имеет меньшее требование выравнивания, чем максимальное требование выравниваниядля вашей реализации.Тогда его размер не может быть кратным макс.выравнивание и т. д., когда вы пытаетесь привести / использовать buf + header_size в качестве указателя на то, что имеет макс.выравнивание, это смещено.Что касается C, это неопределенное поведение.На Intel это работает, но медленнее.На некоторых ARM это вызывает аппаратное исключение.На некоторых ARM это молча дает неправильный ответ.Поэтому, если вы не хотите делать предположения о платформе в своем коде, вы должны разобраться с этим.

Есть в основном три уловки, которые я знаю, чтобы убедиться, что ваш заголовок не вызывает смещения:

  • Используйте специфическую для реализации прагму выравнивания, чтобы вызвать проблему.
  • Используйте платформенные знания о структуре и выравнивании структуры, чтобы убедиться, что ее размер как раз кратенот максимального требования выравнивания в системе.Как правило, это означает, что «добавьте дополнительный int в качестве отступа, если необходимо сделать его кратным 8, а не просто 4-кратным».
  • Сделать заголовок union каждого стандартного типа,вместе со структурой, которую вы на самом деле хотите использовать.Хорошо работает в C, но у вас могут возникнуть проблемы в C ++, если ваш заголовок недопустим для членства в союзах.

В качестве альтернативы, вы можете просто определить header_size, а не sizeof(header),но чтобы быть таким, округленный до кратного некоторой небольшой степени 2, это «достаточно хорошо».Если вы тратите немного памяти, пусть будет так, и у вас всегда может быть «заголовок переносимости», который определяет такие вещи не только в зависимости от платформы, но и позволяет легко адаптироваться к новым платформам.

[*], за исключением случаев, когда SIMD слишком большого размера.Так как они нестандартные, и было бы расточительно выравнивать каждое распределение только из-за них, они отмахивались от руки, и вам нужны специальные функции распределения для них.

2 голосов
/ 20 августа 2010

Я тоже поиграл с выравниванием памяти. Первое, что нужно понять, - это то, что у каждого объекта есть свои требования к выравниванию памяти, которые (не более) соответствуют размеру самого объекта. К счастью, это часто меньше.

Существуют средства двух типов, помогающие писать правильные распределители памяти (C ++ 0x, их можно найти в std::tr1 в большинстве STL):

  • std::alignment_of дает выравнивание как значение времени компиляции
  • std::aligned_storage дает выровненное хранилище в соответствии с его параметрами

Работая с ними двумя, вы получите то, что вам нужно.

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

template <class T, size_t N>
struct Block
{
  // Header bits

  typedef std::aligned_storage<
      N*sizeof(T),
      std::alignment_of<T>::value
  >::type buffer_type;
  buffer_type mBuffer;
};

Две ноты:

  • Block само по себе может иметь другое выравнивание, но это не имеет значения
  • Вы можете использовать sizeof для выравнивания, оно гарантированно сработает, даже если немного расточительно

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

2 голосов
/ 20 августа 2010

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

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

1 голос
/ 20 августа 2010

Насколько я знаю, boost :: pool основана на «распределителе малых объектов», который объясняет Александр Александрович в своей книге «Современный дизайн на языке c ++».Я должен читать книгу (так как ты тоже пишешь этот материал для изучения)

1 голос
/ 20 августа 2010

Используйте http://msdn.microsoft.com/en-us/library/bb982233.aspx - std :: alignment_of. Это гарантирует, что для каждого T, который вы выделяете в свой пул, вы будете знать выравнивание и сможете гарантировать его соответствие. Я не собираюсь притворяться, что знаю / понимаю фактическую логику, которую вы использовали бы, чтобы превратить эту информацию в гарантии выравнивания, но она существует и доступна для использования.

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