Подтвердить что ты не робот

Определение максимально возможного выравнивания в С++

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

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

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

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

Идеи?

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

4b9b3361

Ответ 1

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

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

Неясно, будут ли типы SSE рассматриваться как типы объектов С++.

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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];
};

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

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

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

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

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

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

Ответ 6

Это то, что я использую. В дополнение к этому, если вы распределяете память, тогда новый() 'd массив 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
            };
        }