Определение максимально возможного выравнивания в C ++ - PullRequest
21 голосов
/ 06 октября 2009

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

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

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

Если все остальное терпит неудачу, я знаю, что выделение массива char с new гарантированно будет иметь максимальное выравнивание, но с шаблонами TR1 / C ++ 0x alignment_of и aligned_storage, мне интересно, будет ли это можно создать буфер на месте в моем классе буферов, вместо того, чтобы требовать дополнительного перенаправления указателя динамически размещаемого массива.

Идеи

Я понимаю, что существует множество вариантов определения максимального выравнивания для ограниченного набора типов: объединение или просто alignment_of из TR1, но моя проблема заключается в том, что набор типов не ограничен. Я не знаю заранее, какие объекты должны храниться в буфере.

Ответы [ 6 ]

12 голосов
/ 18 апреля 2013

В C ++ 11 std :: max_align_t, определенный в заголовке, cstddef - это тип POD, требования к выравниванию которого, по крайней мере, такие же строгие (такие же большие), как и требования для каждого скалярного типа.

Используя новый оператор alignof, это будет так же просто, как alignof(std::max_align_t)

10 голосов
/ 30 декабря 2010

В C ++ 0x параметр шаблона Align std::aligned_storage<Len, Align> имеет аргумент по умолчанию «выравнивание по умолчанию», который определяется как (N3225 §20.7.6.6 Таблица 56):

Значение выравнивания по умолчанию должно быть самым строгим требованием выравнивания для любого типа объекта C ++, размер которого не превышает Len.

Не ясно, будут ли типы SSE считаться «объектными типами C ++».

Аргумент по умолчанию не был частью TR1 aligned_storage; он был добавлен для C ++ 0x.

5 голосов
/ 27 июня 2010

К сожалению, обеспечение максимального выравнивания намного сложнее, чем должно быть, и нет никаких гарантированных решений AFAIK. Из блога GotW ( Fast Pimpl article ):

union max_align {
  short       dummy0;
  long        dummy1;
  double      dummy2;
  long double dummy3;
  void*       dummy4;
  /*...and pointers to functions, pointers to
       member functions, pointers to member data,
       pointers to classes, eye of newt, ...*/
};

union {
  max_align m;
  char x_[sizeofx];
};

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

Это о ближайшем "взломе", который я знаю для этого.

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

Я использую это для Пупырышки особенно. Однако просто иметь распределитель недостаточно; чтобы такой распределитель работал, мы должны предположить, что память для класса Foo выделена в конструкторе, эта же память также освобождается только в деструкторе, и что сам Foo создается в стеке. Чтобы сделать его безопасным, мне нужна была функция, чтобы увидеть, находится ли указатель 'this' класса в локальном стеке, чтобы определить, можем ли мы использовать наш сверхбыстрый распределитель стека на основе кучи. Для этого нам пришлось исследовать решения для конкретных ОС: я использовал TIB и TEB для Win32 / Win64, а мои коллеги нашли решения для Linux и Mac OS X.

Результатом, после недели исследования методов, специфичных для ОС, для определения диапазона стека, требований к выравниванию и выполнения большого количества тестирования и профилирования, стал распределитель, который мог распределить память за 4 такта в соответствии с нашими тестами счетчиков тиков как в отличие от 400 циклов для нового malloc / оператора (наш тест включал конфликт потоков, поэтому malloc, вероятно, будет немного быстрее, чем это в однопоточных случаях, возможно, пару сотен циклов). Мы добавили стек кучи для каждого потока и обнаружили, какой поток использовался, что увеличило время примерно до 12 циклов, хотя клиент может отслеживать распределитель потоков, чтобы получить распределение по 4 циклам. Это стерло горячие точки на основе распределения памяти с карты.

Несмотря на то, что вам не нужно проходить через все эти проблемы, написание быстрого распределителя может быть проще и в более общем смысле (например, позволяет определять объем памяти, выделяемый / освобождаемый во время выполнения), чем что-то вроде max_align Вот. max_align достаточно прост в использовании, но если вам не хватает скорости для выделения памяти (и при условии, что вы уже профилировали свой код и нашли горячие точки в malloc / free / operator new / delete с основными участниками, находящимися в коде, которым вы управляете более), написание собственного распределителя может действительно изменить ситуацию.

5 голосов
/ 06 октября 2009

За исключением некоторого типа maximally_aligned_t, который все компиляторы обещали добросовестно поддерживать для всех архитектур во всем мире, я не понимаю, как это можно решить во время компиляции. Как вы говорите, множество потенциальных типов не ограничено. Неужели дополнительная косвенная указатель действительно важна?

1 голос
/ 07 октября 2009

Выделение выровненной памяти сложнее, чем кажется - см., Например, Реализация выровненного выделения памяти

0 голосов
/ 21 марта 2012

Это то, что я использую. В дополнение к этому, если вы выделяете память, то новый массив () с типом char, длина которого больше или равна max_alignment, будет выровнен по max_alignment, поэтому вы можете использовать индексы в этом массиве для получения выровненных адресов.

enum {
            max_alignment = boost::mpl::deref<
                boost::mpl::max_element<
                        boost::mpl::vector<
                            boost::mpl::int_<boost::alignment_of<signed char>::value>::type,
                            boost::mpl::int_<boost::alignment_of<short int>::value>::type,
                            boost::mpl::int_<boost::alignment_of<int>::value>::type,                                boost::mpl::int_<boost::alignment_of<long int>::value>::type,
                            boost::mpl::int_<boost::alignment_of<float>::value>::type,
                            boost::mpl::int_<boost::alignment_of<double>::value>::type,
                            boost::mpl::int_<boost::alignment_of<long double>::value>::type,
                            boost::mpl::int_<boost::alignment_of<void*>::value>::type
                        >::type
                    >::type
                >::type::value
            };
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...